Search code examples
haskellmonad-transformersapplicative

Examples of Haskell Applicative Transformers


The wiki on www.haskell.org tells us the following about Applicative Transformers:

So where are applicative transformers? The answer is, that we do not need special transformers for applicative functors since they can be combined in a generic way. http://www.haskell.org/haskellwiki/Applicative_functor#Applicative_transfomers

I tried the following in order to try to combine a bunch of applicative functors. But all I got was bunch of errors. Here is the code:

import Control.Applicative
import System.IO

ex x y = (:) <$> x <*> y 
test1 = ex "abc" ["pqr", "xyz"]  -- only this works correctly as expected
test2 = ex "abc" [Just "pqr", Just "xyz"]
test3 = ex "abc" (Just "pqr")
test4 = ex (Just 'a') ["pqr", "xyz"]
test5 = ex (return ("abc"):: IO ()) [Just "pqr", Just "xyz"]

This produces a lot of type errors, which though I can partially understand, I couldn't resolve them at all.

The errors are given at the end.

So, how do I combine the Maybe Applicative and the List Applicative for example?

How do I combine the State Applicative and the List Applicative for example? Are there any other examples, let's say, combining Maybe and List, Maybe and State and finally the dreadful of all the IO and State applicatives?

Thanks.

The GHCi error msgs follow.

example.hs:6:19:
    Couldn't match expected type `[Char]' with actual type `Maybe a0'
    In the return type of a call of `Just'
    In the expression: Just "pqr"
    In the second argument of `ex', namely `[Just "pqr", Just "xyz"]'

example.hs:7:19:
    Couldn't match expected type `[[Char]]' with actual type `Maybe a0'
    In the return type of a call of `Just'
    In the second argument of `ex', namely `(Just "pqr")'
    In the expression: ex "abc" (Just "pqr")

example.hs:8:23:
    Couldn't match expected type `Maybe' with actual type `[]'
    In the second argument of `ex', namely `["pqr", "xyz"]'
    In the expression: ex (Just 'a') ["pqr", "xyz"]
    In an equation for `test4': test4 = ex (Just 'a') ["pqr", "xyz"]

example.hs:9:21:
    Couldn't match expected type `()' with actual type `[Char]'
    In the first argument of `return', namely `("abc")'
    In the first argument of `ex', namely `(return ("abc") :: IO ())'
    In the expression:
      ex (return ("abc") :: IO ()) [Just "pqr", Just "xyz"]
Failed, modules loaded: none.
Prelude>

Solution

  • The wiki article says that liftA2 (<*>) can be used to compose applicative functors. It's easy to see how to use it from its type:

    o :: (Applicative f, Applicative f1) =>
         f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
    o = liftA2 (<*>)
    

    So to if f is Maybe and f1 is [] we get:

    > Just [(+1),(+6)] `o` Just [1, 6] 
    Just [2,7,7,12]
    

    The other way around is:

    >  [Just (+1),Just (+6)] `o` [Just 1, Just 6]
    [Just 2,Just 7,Just 7,Just 12]
    

    As @McCann said your ex function is equivalent to liftA2 (:):

    test1 = liftA2 (:) "abc" ["pqr", "xyz"]
    

    To use (:) with deeper applicative stack you need multiple applications of liftA2:

    *Main> (liftA2 . liftA2) (:) (Just "abc") (Just ["pqr", "xyz"])
    Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]
    

    However it only works when both operands are equally deep. So besides double liftA2 you should use pure to fix the level:

    *Main> (liftA2 . liftA2) (:) (pure "abc") (Just ["pqr", "xyz"])
    Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]