Colin Woodbury
Colin Woodbury

Reputation: 1809

Monadic expressions in conditionals - GHC compiles, cabal refuses

Been scratching my head for a day over this one.

I have a few functions in my code that look like this:

function :: IO (Maybe Whatever)
function = do
   monadFun
   yaySomeIO
   status <- maybeItWillFail
   if checkStatus status  -- Did we succeed?
   then monadTime >>= return . Just . processItPurely
   else return Nothing

ghci will load and run this interactively with no problems, and ghc will compile it happily. Running this through cabal, however, gives me this:

myProgram.hs:94:16:
Unexpected semi-colons in conditional:
    if checkStatus status; then monadTime >>= return . Just . processItPurely; else return Nothing

Perhaps you meant to use -XDoAndIfThenElse?

And whatever this -XDoAndIfThenElse option is, I can't seem to find a trace of it anywhere in any documentation. Why is cabal (or is this ghc by this point?) yelling at me for using semi-colons that IT put there in the first place? Or is using monadic expressions in if-then-else statements just a bad idea?

Note that cabal doesn't complain about this at all:

case checkStatus status of
   True -> monadTime >>= return . Just . processItPurely
   _    -> return Nothing

...except this is ugly as hell and I'd never want to put this in my code. Can anyone tell me what's going on? Please and thanks in advance.

Upvotes: 15

Views: 554

Answers (2)

Dan Burton
Dan Burton

Reputation: 53715

This isn't a direct answer to your question, but you can eliminate the if statement by taking advantage ofMaybeT. Also, foo >>= return . bar is the same as bar <$> foo. (<$> is from Control.Applicative, and is the same as fmap)

function :: MaybeT IO Whatever
function = do
   lift monadFun
   lift yaySomeIO
   status <- lift maybeItWillFail
   guard (checkStatus status)
   processItPurely <$> lift monadTime

The only annoyance is the gratuitous sprinkling of lifts, but there are ways to get rid of those.

Upvotes: 6

hammar
hammar

Reputation: 139930

The "correct" way of indenting if-expressions in a do-block is to indent the else and then lines further than the if, like this.

function = do
   monadFun
   yaySomeIO
   status <- maybeItWillFail
   if checkStatus status  -- Did we succeed?
      then monadTime >>= return . Just . processItPurely
      else return Nothing

This is because lines with the same amount of indentation in a do block are normally treated as separate statements.

However, there is an extension called DoAndIfThenElse which will allow you to write it the way you did. This extension was made standard in Haskell 2010, which is why GHC enables it by default.

Cabal tends to require you to be more explicit about these things, so to use it in Cabal, you need to either mention it in your .cabal file or add {-# LANGUAGE DoAndIfThenElse #-} to the top of your module.

Upvotes: 27

Related Questions