Reputation: 3255
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
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