Search code examples
haskellfunctor

How does "(\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5 " really work?


In this example from Learn you a Haskell, the author showed how to declare an instance of Applicative for function ->r

instance Applicative ((->) r) where  
    pure x = (\_ -> x)  
    f <*> g = \x -> f x (g x)  

Later on, he gave a concrete example

ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5  
[8.0,10.0,2.5]  

I try to reason this snippet step by step

  1. Seeing (+3) <*> (*2) <*> (/2) as f <*> g <*> h and thinking that f <*> g <*> h returns a function \x -> f x (g x (h x)), I thought (+3) <*> (*2) <*> (/2) returns a function \x -> x+3 (x*2 (x/2)). But I can not do :t (+3) <*> (*2) <*> (/2) in ghci, which raises this error
error:
    • Occurs check: cannot construct the infinite type:
        a1 ~ a -> a1 -> b
      Expected type: (a -> a1 -> b) -> a1
        Actual type: a1 -> a1
    • In the second argument of ‘(<*>)’, namely ‘(/ 2)’
      In the expression: (+ 3) <*> (* 2) <*> (/ 2)
  1. If (+3) <*> (*2) <*> (/2) does return a function \x -> x+3 (x*2 (x/2)), how does it pattern match with the lambda function \x y z -> [x,y,z]? (Actually I am still having some trouble to understand lambda functions except for the really basic simple ones.)

Solution

  • I think you got the precedence wrong. The expression

    (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5  
    

    does not involve as a subexpression the one you mention

    (+3) <*> (*2) <*> (/2)
    

    since it should be parsed associating to the left, as follows:

    ((((\x y z -> [x,y,z]) <$> (+3)) <*> (*2)) <*> (/2)) $ 5
    

    Here, we start to simplify from the left:

    (\x y z -> [x,y,z]) <$> (+3)
    = { def. <$> }
    (\a y z -> [(+3) a,y,z])
    

    Then, we proceed:

    (\a y z -> [(+3) a,y,z]) <*> (*2)
    = { def. <*> }
    (\a z -> [(+3) a, (*2) a,z])
    

    Finally,

    (\a z -> [(+3) a, (*2) a,z]) <*> (/2)
    = { def. <*> }
    (\a -> [(+3) a, (*2) a, (/2) a])
    

    As a last step, we let a to be 5, obtaining [5+3, 5*2, 5/2].