4

I've been working through the excellent Programming in Haskell (2nd ed). I'm a bit flummoxed by the question on Applicatives, though.

Given the following type:

data Expr a = Var a | Val Int | Add (Expr a) (Expr a) deriving Show

The question is to write the implementations of the Functor, Applicative and Monad classes.

Functor is straightforward, as is Monad (at least it compiles, I've not fully wrapped my head around it yet but I'm more worried about this bit first).

I've come up with this, which compiles, but I have concerns:

instance Applicative Expr where
    -- pure :: a -> Expr a
    pure = Var

    -- (<*>) :: Expr (a -> b) -> Expr a -> Expr b
    (Var fab) <*> fa = fmap fab fa

pure is fine, but I'm worried about the actual applicative operator <*>. As far as I can tell, it only makes sense for Var - not for Val or Add. And it just seems weird to me that you'd have a type where's it's legal to express things that can explode - for instance, you could have Add (Var ord) (Val 10) which is of type Expr (Char -> Int), so would typecheck as the lhs in a <*> expression, but (as it stands) would explode. And it's not clear to me how the recursive definition would work - because as soon as you hit (Val 10), you're stuffed - there's no way to convert the rhs to the necessary type.

What am I missing here? How do I complete the definition of <*> such that things don't explode, and it's still a valid applicative? Am I right in thinking/feeling that in reality, you wouldn't design a type like this?

Thanks!

retnuH
  • 1,525
  • 2
  • 11
  • 18
  • 3
    If you have a `Monad` instance already, you can just define `(<*>) = ap` (from `Control.Monad`). You could unroll the definition `ap fab fa` to find out how it works. – luqui Jan 18 '19 at 18:14
  • 1
    This is indeed a case where defining `fmap`, `return` and `join` is very simple and intuitive. That can then be used to build a monad. By comparison defining the applicative instance feels far less natural: I'd just use `(<*>)=ap` as luqui suggests and forget about it :-P – chi Jan 18 '19 at 19:41
  • 1
    Still learning about all these things so I only worked on `bind`/`>>=` - I'm only vaguely aware of `ap` and `join`, haven't really got into them. I remember that there's some stuff where you can define some in terms of the others and vice versa. Hopefully the book will go into it some more or another thorough going through the Typeclassopdia after finishing the book will help connect the dots. And, you know, writing some actual code :) – retnuH Jan 18 '19 at 23:36

1 Answers1

3
Val :: Int -> Expr a

for any a. So

Val x <*> _ = Val x

is valid, as is

_ <*> Val y = Val y

as is

Val x <*> Val y = Val (something about x and y)

so unfortunately now you have a choice, which means you are about to make the wrong one. Fortunately only one of these is compatible with the Monad instance (which one?).

As for the recursive case, you have

Add e e' <*> fb = ...
     -- e :: Expr (a -> b)
     -- e' :: Expr (a -> b)
     -- fb :: Expr a

And you should use all the incoming information to make an Expr b, as well as preserving the "structure" (Add). What are all the ways you could make an Expr b from this (remember you can use the applicative operator recursively)?

luqui
  • 59,485
  • 12
  • 145
  • 204
  • Although, `Val x <*> Val y = ...` isn't really the choice, though, is it? What about `Val x <*> (Add l r)` and `Val x <*> Var r` - as you suggest, there can be only one correct answer, easily verified by the use of `ap` as you suggested elsewhere. – retnuH Jan 18 '19 at 23:32
  • 1
    Great answer, thanks for helping me to build my intuition and understanding without just flat out saying "do this". Really appreciate it! – retnuH Jan 18 '19 at 23:32