61

In a recent answer to a style question, I wrote

main = untilM (isCorrect 42) (read `liftM` getLine)

and

isCorrect num guess =
  case compare num guess of
    EQ -> putStrLn "You Win!" >> return True
    ...

Martijn helpfully suggested alternatives:

main = untilM (isCorrect 42) (read <$> getLine)

EQ -> True <$ putStrLn "You Win!"

Which common patterns in Haskell code can be made clearer using abstractions from Control.Applicative? What are helpful rules of thumb to keep in mind for using Control.Applicative effectively?

Community
  • 1
  • 1
Greg Bacon
  • 134,834
  • 32
  • 188
  • 245

3 Answers3

48

There is a lot to say in answer to your question, however, since you asked, I will offer this "rule of thumb."

If you are using do-notation and your generated values[1] are not used in the expressions that you are sequencing[2], then that code can transform to an Applicative style. Similarly, if you use one or more of the generated values in an expression that is sequenced, then you must use Monad and Applicative is not strong enough to achieve the same code.

For example, let us look at the following code:

do a <- e1
   b <- e2
   c <- e3
   return (f a b c)

We see that in none of the expressions to the right of <- do any of the generated values (a, b, c) appear. Therefore, we can transform it to using Applicative code. Here is one possible transformation:

f <$> e1 <*> e2 <*> e3

and another:

liftA3 f e1 e2 e3

On the other hand, take this piece of code for example:

do a <- e1
   b <- e2 a
   c <- e3
   return (f b c)

This code cannot use Applicative[3] because the generated value a is used later in an expression in the comprehension. This must use Monad to get to its result -- attempt to factor it into Applicative to get a feel for why.

There are some further interesting and useful details on this subject, however, I just intended to give you this rule of thumb whereby you can skim over a do-comprehension and determine pretty quickly if it can be factored into Applicative style code.

[1] Those that appear to the left of <-.

[2] Expressions that appear to the right of <-.

[3] strictly speaking, parts of it could, by factoring out e2 a.

sid-kap
  • 941
  • 1
  • 10
  • 17
Tony Morris
  • 3,045
  • 2
  • 29
  • 17
  • 15
    You can sometimes mix Monad and Applicative operators if you like the applicative style. Your second do block can be written `f <$> (e1 >>= e2) <*> e3` – AndrewC Jan 17 '13 at 08:01
  • 3
    +1 for concrete examples of when Applicative is sufficient, and when Monad is required. – sjy Jul 28 '14 at 09:12
45

Basically, monads are also applicative functors [1]. So, whenever you find yourself using liftM, liftM2, etc., you could chain the computation together using <*>. In some sense, you can think of applicative functors as analogous to functions. A pure function f can be lifted by doing f <$> x <*> y <*> z.

Compared to monads, applicative functors cannot run its arguments selectively. The side effects of all the arguments will take place.

import Control.Applicative

ifte condition trueClause falseClause = do
  c <- condition
  if c then trueClause else falseClause

x = ifte (return True) (putStrLn "True") (putStrLn "False")

ifte' condition trueClause falseClause = 
  if condition then trueClause else falseClause

y = ifte' <$> (pure True) <*> (putStrLn "True") <*> (putStrLn "False")

x only outputs True, whereas y outputs True and False sequentially.

[1] The Typeclassopedia. Highly recommended.

[2] http://www.soi.city.ac.uk/~ross/papers/Applicative.html. Although this is an academic paper, it's not hard to follow.

[3] http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors. Explains the deal very well.

[4] http://book.realworldhaskell.org/read/using-parsec.html#id652399. Shows how the monadic Parsec library can also be used in an applicative way.

j3h
  • 692
  • 5
  • 10
Wei Hu
  • 2,888
  • 2
  • 27
  • 28
  • 4
    That's the only place I've seen where the difference between `Monad` and `Applicative` is explained (in terms of what you can express with each one). Great job! Can we call `ifte` with the applicative functions: `z = ifte <$> ...` ? – Daniel Jan 20 '10 at 21:53
  • 1
    `ifte` is already a monadic function, so there's not much point lifting it one more time. – Wei Hu Jan 20 '10 at 22:53
  • 3
    You can write "ifte' <$> (pure True)" as "ifte' True"—"pure" is rarely necessary in combination with "<$>" and "<*>" (if at all). – Martijn Jan 21 '10 at 12:15
  • 1
    y = ifte' True <*> putStrLn "True" <*> putStrLn "False" – Edward Kmett Feb 04 '10 at 15:31
  • It's `y = ifte' True <$> (putStrLn "True") <*> (putStrLn "False")` when you leave the `pure` away. – David Oct 17 '12 at 13:43
10

See The basics of applicative functors, put to practical work by Bryan O'Sullivan.

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245