0

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.)
  • Not sure if it's fully a dupe, as you're asking a few other things and it's a slightly more complicated example, but I hope the above link is helpful. – Robin Zigmond Mar 07 '19 at 13:47
  • 5
    In the expression 3-2+1, you can't say ”I'm just going to think about the 2+1 first, which is 3, so the whole expression is 3-3, which is 0”. That's wrong because + and - have the same precedence and are left-associative in math. You're making the exact same mistake with <$> and <*> in Haskell. – Joseph Sible-Reinstate Monica Mar 07 '19 at 13:48
  • 1
    Not really an answer, but here's an intuitive overview of what's going on: when you do `func <$> f <*> g <*> h <*> ... $ x`, it gets evaluated as `func (f x) (g x) (h x) ...`. It's very useful when you have some data you want to pass to all parameters of a function. – bradrn Mar 07 '19 at 21:15

2 Answers2

6

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].

chi
  • 111,837
  • 3
  • 133
  • 218
3

That expression is actually parenthesized to the left:

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

which is

=
   let x = (5+3) in (\y z -> [x,y,z])   (5*2)      (5/2)
=
   let x = (5+3) ; y = (5*2)  in  (\z -> [x,y,z])   (5/2)
=
   let x = (5+3) ; y = (5*2) ; z = (5/2)  in   [x,y,z]    
Will Ness
  • 70,110
  • 9
  • 98
  • 181