1

The problem description

It seems that something has changed in Haskell since the publication of Learn you a Haskell for Great Good. When creating Monads you not only have to define the Functor but also now the Applicative implementation.

In Chapter 14 of the book, in the section on "Making Monads", we define a newtype Prob like...

newtype Prob a = Prob { getProb :: [(a, Rational)] } deriving Show

and then its Functor instance...

instance Functor Prob where
  fmap f (Prob xs) = Prob $ map (\(x, p) -> (f x, p)) xs

and then the book goes on to define the Monad instance for it...

flatten :: Prob (Prob a) -> Prob a
flatten (Prob xs) = Prob $ concat $ map multAll xs
  where multAll (Prob innerxs, p) = map (\(x,r) -> (x,r*p)) innerxs

instance Monad Prob where
  return x = Prob [(x,1%1)]
  m >>= f = flatten (fmap f m)
  fail _ = Prob []

but I get an error...

• No instance for (Applicative Prob) arising from the superclasses of an instance declaration

• In the instance declaration for ‘Monad Prob’

I've read that the Applicative instance is now explicitly required and created my solution and wondered if it is correct?

Current attempt at implementing it myself

My implementation is...

instance Applicative Prob where
  pure x = Prob [(x, 1%1)]
  (Prob xs) <*> (Prob ys) = Prob ([(f y, p * q) | (f, p) <- xs, (y, q) <- ys])

I've done this as the first parameter of the <*> could be something like Prob [((*3),1%2),((+1),1%2)] so it would seem that each function in the first parameter would have to be applied to every value in the second parameter and that also the probabilities would have to be dealt with somehow.

Request for answers

Which just seems wrong to me, I don't like that I've pulled out the probabilities like this when the implementation in the book was just automatic and didn't require any of this extra work. I wondered if there is a simpler way to define what I have created or even if this is just wrong in the first place? I feel like there is possibly an fmap hiding in my Applicative code somewhere but I'm too new to find it myself.

Even better would be if there is an updated resource for these missing implementations which I've, as yet, been unsuccessful in finding.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • 2
    LYAH is a great book, but it is sadly out of date. I vaguely recall someone forking the contents in order to update things like this. – chepner May 23 '21 at 22:25
  • @chepner I have come to learn this over my time of reading it. It makes it interesting trying to get through the examples and stumbling across obstacles along the way. Keeps me on my toes. :D – Fogmeister May 23 '21 at 22:27
  • 1
    Once you have defined your own instance it's good to know if it can be derived. If you enabled `-XDerivingVia` you can write `deriving (Functor, Applicative, Monad, MonadFail) via WriterT (Product Rational) []`. Uses the fact that [`WriterT [] ..`](https://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-Writer-Lazy.html#t:WriterT) has the same runtime representation as `Prob`. [`Product Rational`](https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Monoid.html#t:Product) says to use `Rational` with multiplication and 1 (= 1%1) as mempty. – Iceland_jack May 26 '21 at 17:36
  • 1
    But it is not limited to functor, applicative, monad. Running the [`:instances WriterT [] (Product Rational)`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/ghci.html#ghci-cmd-:instances) command lists candidates for deriving like `Alternative`, `MonadZip`, `MonadFix`. – Iceland_jack May 26 '21 at 17:40

2 Answers2

4

If you have a Monad instance for m, you get an Applicative instance of m for free defined by

instance Applicative m where
    pure  = return
    (<*>) = ap

Recall that ap is defined by

ap mf mx = mf >>= \f -> x >>= f

Or, using do notation,

ap mf mx = do 
    { f <- mf
    ; x <- mx
    ; return (f x)
    }

Applicatives are generalisations of Monads. Every Monad must also be an Applicative in this canonical way. Modern Haskell enforces this by requiring an Applicative instance for every Monad.

Mark Saving
  • 1,752
  • 7
  • 11
  • Ah! OK, that makes sense now. I was getting the hierarchy the wrong way round. And actually, what you have said makes sense as I couldn't help but thinking that my <*> was duplicating a lot of the logic in the Monad instance (multiplying the rationals etc...). Thanks very much. – Fogmeister May 23 '21 at 22:24
  • 1
    @Fogmeister I suspect you did not have the hierarchy the wrong way around. It's just that, unlike some other languages' systems, the "must exist" hierarchy and the "must use in its implementation" hierarchy are decoupled -- an `Applicative` instance must exist for a `Monad` instance to be legal, but either one can call methods from the other in its instance implementation. – Daniel Wagner May 23 '21 at 23:44
2

LYAH was published long before Applicative was made a super class of Monad.At that time, the classes were defined like

 class Functor where
     ...

 class Functor f => Applicative f where
     ...

 class Functor m => Monad m where
     ...

Now, Applicative has been inserted between Functor and Monad:

 class Functor where
     ...

 class Functor f => Applicative f where
     ...

 class Applicative m => Monad m where
     ...

Before, you would typically define a new Applicative instance in terms of existing Functor and Monad instances. Now, you tend to define Applicative in terms of Functor, and Monad in terms of Applicative.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • @DanielWagner "Incorrect" seems strong. The "must" claim you are refuting does not appear in this answer. The answer just claims a way things are "typically done". Personally I'm a little skeptical even of that - I quite often see Applicative defined in terms of Monad. But I don't have enough data to say for certain. Do you? – amalloy May 24 '21 at 00:14
  • @amalloy You are absolutely right. I've deleted my comment, therefore. – Daniel Wagner May 24 '21 at 00:28
  • @amalloy @DanielWagner Sure, for existing code it would be easier to define the "newfangled" `Applicative` instance in terms of an already written `Monad` instance. I was under the impression, though I could be (and probably am) wrong, that for types after the F/A/M proposal took effect, the people would write `Applicative` first and then `Monad`. – chepner May 24 '21 at 00:52
  • @chepner I think in practice Monad is often easier to understand. I'm not really sure why - maybe easier to discharge one wrapper at a time, than to figure out how to combine two wrappers? – amalloy May 24 '21 at 01:47