Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monadic expressions in conditionals - GHC compiles, cabal refuses

Tags:

haskell

cabal

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.

like image 535
Colin Woodbury Avatar asked Jun 03 '12 01:06

Colin Woodbury


2 Answers

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.

like image 66
hammar Avatar answered Oct 14 '22 05:10

hammar


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.

like image 27
Dan Burton Avatar answered Oct 14 '22 05:10

Dan Burton