Question

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.

Was it helpful?

Solution

Why not just leverage the fact that fail _ = Nothing for the Maybe monad?

mthree <- lift $ getInputLine "dessert> "
notNothing mthree

becomes

Just mthree <- lift $ getInputLine "dessert> "

OTHER TIPS

How about a helper function?

inputLine :: String -> MaybeT (InputT IO) String
inputLine prompt = do
    m <- lift $ getInputLine prompt
    case m of
        Just x -> return x
        Nothing -> mzero

This can be shortened considerably using various tricks, but I wanted to be clear. Now you can just forget that getInputLine can fail, MaybeT takes care of that for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top