7

Given the following data type:

data Tree a =
    Branch (Tree a) (Tree a)
  | Leaf a deriving (Eq, Show)

And the following Functor instance :

instance Functor Tree where
  fmap f (Leaf a)       = Leaf $ f a
  fmap f (Branch t1 t2) = Branch (fmap f t1) (fmap f t2)

How to implement best the Applicative instance for this tree? I came up with:

instance Applicative Tree where
  pure = Leaf

  Leaf f       <*> t            = f <$> t
  Branch t1 t2 <*> Leaf a       = t1 <*> Leaf a
  Branch t1 t2 <*> Branch t3 t4 = Branch (t1 <*> t3) (t2 <*> t4)

Even if it compiles, I'm very suspicious about this implementation. I don't know if this Branch (Leaf (+1)) (Leaf (+2)) <*> Leaf 7 should return Leaf 8 (find the closest function to apply) or duplicate and return Branch (Leaf 8) (Leaf 9).

MMacphail
  • 541
  • 3
  • 19

1 Answers1

7

Even if it compiles, I'm very suspicious about this implementation. I don't know if this Branch (Leaf (+1)) (Leaf (+2)) <*> Leaf 7 should return Leaf 8 (find the closest function to apply) or duplicate and return Branch (Leaf 8) (Leaf 9)

Reasonable instances should follow Applicative functor laws and one of them is:

u <*> pure y = pure ($ y) <*> u -- Interchange

i.e.

Branch t1 t2 <*> Leaf a

should be the same as:

pure ($ a) <*> Branch t1 t2

But according to this implementation:

Leaf f <*> t = f <$> t

It should be equal to:

($ a) <$> Branch t1 t2

i. e

Branch (fmap ($ a) t1) (fmap ($ a) t2)

Hence, in the particular case of Branch (Leaf (+1)) (Leaf (+2)) <*> Leaf 7, it should return:

Branch (Leaf 8) (Leaf 9)
Igor Drozdov
  • 14,690
  • 5
  • 37
  • 53
  • Thank you for your answer! What does ($ a) mean ? – MMacphail Aug 15 '18 at 09:54
  • 1
    The link I've provided (about laws) contains this information: ($ y) is the function that supplies y as argument to another function. https://en.wikibooks.org/wiki/Haskell/Higher-order_functions#Application – Igor Drozdov Aug 15 '18 at 10:00
  • 3
    @MMacphail `($ a)` is the same as `(\f -> f a)` the function which takes a function `f` and applies it to `a`. – chi Aug 15 '18 at 10:10
  • @MMacphail, more generally, if `*&*` is an operator, `(*&* y)` (called an *operator section*) is the same as `\x -> x *&* y`. In this case, the operator is `$`, defined `f $ a = f a`. You can use an operator on the other side, too: `(x *&*) = \y -> x *&* y` – dfeuer Aug 15 '18 at 15:39
  • @IgorDrozdov Is the last equation in author's code correct? Namely, `Branch t1 t2 <*> Branch t3 t4 = Branch (t1 <*> t3) (t2 <*> t4)` ... Intuitively it doesn't feel right and `Branch t1 t2 <*> t = Branch (t1 <*> t) (t2 <*> t)` feels better but I cannot come up with any example or proof that would support this feeling. – micsza Jun 11 '19 at 20:14