2

in my program I use a function if' defined in one of the modules instead of the built-in if-then-else construct. It is defined trivially and works just fine.

However, there's one place in code where I need to apply it to monad values (IO in my case), i.e. the type signature should look somewhat like IO Bool -> IO a -> IO a -> IO a. Naturally, I tried to lift it.

if' <$> mb <*> action1 <*> action2

But when I try to evaluate the expression, I don't get what I expect.

*Main> if' <$> return True <*> putStrLn "yes" <*> putStrLn "no"
yes
no

I know that <*> description reads „sequential application“, so maybe it's that. But what is happening here? Can I fix it without writing a whole new dedicated function?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Xyzzy1201
  • 23
  • 2
  • In the last expression, the `if` statement only controls the value that is returned - both branches must be executed in order to even compute those values, because it uses the applicative interface to IO. You probably want `return True >>= \b -> if' b (putStrLn "yes") (putStrLn "no")` - note how `if'` is applied *directly* to the different branches. – user2407038 Mar 06 '16 at 14:48

1 Answers1

3

(<*>) evaluates both of its arguments, so it effectively a pipeline of applications lifted over some applicative. The ability to inspect values and change the future of the computation is the extra power that the Monad class has over Applicative so you need to use that instead e.g.

mif' :: Monad m => m Bool -> m a -> m a -> m a
mif' bm xm ym = bm >>= (\b -> if b then xm else ym)
Lee
  • 142,018
  • 20
  • 234
  • 287
  • 1
    @Carl has a great answer pertaining to this at [https://stackoverflow.com/questions/17409260/what-advantage-does-monad-give-us-over-an-applicative](https://stackoverflow.com/questions/17409260/what-advantage-does-monad-give-us-over-an-applicative) – badcook Mar 06 '16 at 18:01