I am using the Haskeline package and I want to get three strings in a row from the command line before I do anything and I have come up with what seems to be a neat solution to me. But I am sure that there might be a better way to do it. I am looking for best practices while using the Haskeline package. Please evaluate the merits of the following example code:
import System.Console.Haskeline
import Control.Monad.Trans
import Control.Monad.Maybe
import Data.Maybe
import Control.Monad
main :: IO ()
main = runInputT defaultSettings (runMaybeT getStrings) >>= print
getStrings :: MaybeT (InputT IO) (String, String, String)
getStrings = do
mone <- lift $ getInputLine "food> "
notNothing mone
mtwo <- lift $ getInputLine "drink> "
notNothing mtwo
mthree <- lift $ getInputLine "dessert> "
notNothing mthree
return (fromJust mone, fromJust mtwo, fromJust mthree)
where
notNothing a = guard (a /= Nothing)
As you can see it accomplishes the task of early termination but it looks a bit yucky still. I'm thinking of trying to convert the notNothing's and the getInputLine's into a single line like:
mone <- notNothing =<< lift $ getInputLine "food> " -- does not type check
Which I think does not look that bad. I think that is pretty clear and concise (though it does not type check so I will have to write a version that does).
However, this is the best I have come up with and my question finally is: How would you go about improving this code to be neater and more readily readable? Am I even on the right track?
Edit: If your guard is something other than 'a /= Nothing' then a nice helper function that I just discovered is:
myGuard s = guard (someConditionFunc s) >> s
Because then you can write (as luqui suggested):
mone <- myGuard =<< (lift $ getInputLine prompt)
Which is pretty cool. But if you are matching against only Nothing then TomMD's answer is better.
Why not just leverage the fact that
fail _ = Nothing
for the Maybe monad?becomes