Tarick Welling
Tarick Welling

Reputation: 3255

Multiple arguments on applicatives not working?

So I'm trying to learn about monads, functors and applicatives. I've created the following renamed mirror match of Maybe called Sometimes. (I did this to learn about these things)

data Sometimes a = Nope | Thing a deriving Show

instance Monad Sometimes where
    (Thing x) >>= f = f x
    Nope  >>= f = Nope
    return      = Thing

instance Applicative Sometimes where
      pure = Thing
      Nope <*> _ = Nope
      (Thing g) <*> mx = fmap g mx

instance Functor Sometimes where
     fmap _ Nope = Nope
     fmap g (Thing x) = Thing (g x)

So when I do the following it works:

pure (1+) <*> (Thing 1)
> Thing 2

pure (+) <*> (Thing 1) <*> (Thing 1)
> Thing 2

But if I try three additions it doesn't work:

pure (+) <*> (Thing 1) <*> (pure 1) <*> (pure 1)

<interactive>:108:1: error:
    • Non type-variable argument in the constraint: Num (a -> b)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall a b. (Num (a -> b), Num a) => Sometimes b

Why doesn't this work? I would expect the first two to be applied and then the third to be applied to the result of the first two. My book talks about how implementing fmap0, fmap1, fmap2... is inefficient and as such

... for functions with any desired number of arguments can be constructed in terms of two basic functions with the following types: pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b

And states further on:

A typical use of pure and <*> has the following form:

pure g <*> x1 <*> x2 <*> ... <*> xn

As such I'm expecting it to work but I'm clearly missing something in my definitions/usage of the Applicative.


I'm using the book Programming in Haskell SE by Graham Hutton

Upvotes: 2

Views: 144

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476659

The reason this does not work is because (+) sums two numbers, not three.

You can make a function that sums three numbers, for example with:

pure (\x y z -> x+y+z) <*> (Thing 1) <*> (pure 1) <*> (pure 1)

this then gives us:

Prelude> pure (\x y z -> x+y+z) <*> (Thing 1) <*> (pure 1) <*> (pure 1)
Thing 3

Why doesn't this work? I would expect the first two to be applied and then the third to be applied to the result of the first two.

Exactly, but after the first two are applied, this is no longer a function, but a Num a => Sometimes a. Indeed, if we determines the types, we see that Thing (+) :: Num a => Sometimes (a -> a -> a) and Thing 1 :: Num b => Sometimes b, so that means that Thing (+) <*> Thing 1 has type Num a => Sometimes (a -> a).

Then we determine the type of Thing (+) <*> Thing 1 <*> Thing 1, since Thing (+) <*> Thing 1 has type Num a => Sometimes (a -> a), and the last Thing 1 has type Num c => Sometimes c, it means that Thing (+) <*> Thing 1 <*> Thing 1 has type Num a => Sometimes a, but this is not a function, unless there is a Num type that is a function, which is what the error is saying.

Upvotes: 6

Related Questions