2

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

asked Jun 14, 2020 at 12:13
1
  • 3
    The reason this does not work is because (+) sums two numbers, not three. Commented Jun 14, 2020 at 12:17

1 Answer 1

6

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.

answered Jun 14, 2020 at 12:18
Sign up to request clarification or add additional context in comments.

2 Comments

Aha this must be use of partial function application (currying)? g <*> a <*> b will result in (g a) <*> b and thus a g a b?
@TarickWelling: well Thing g <*> Thing a <*> Thing b will result in Thing (g a b), indeed.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.