-4

I looked at past discussion but could not see why any of the answers are actually correct.

Applicative

<*> :: f (a -> b) -> f a -> f b

Monad

(>>=) :: m a -> (a -> m b) -> m b

So if I get it right, the claim is that >>= cannot be written by only assuming the existence of <*>

Well, let's assume I have <*>.

And I want to create >>=.

So I have f a.

I have f (a -> b).

Now when you look at it, f (a -> b) can be written as (a -> b) (if something is a function of x, y , z - then it's also a function of x, y).

So from the existence of <*> we get (a -> b) -> f a -> f b which again can be written as ((a -> b) -> f a) -> f b, which can be written as (a -> f b).

So we have f a, we have (a -> f b), and we know that <*> results in f b, so we get:

f a -> (a -> f b) -> f b

which is a monad.

Actually, in a more intuitive language: when implementing <*>, I extract (a -> b) out of f(a -> b), I extract a out of f a, and then I apply (a -> b) on a and get b which I wrap with f to finally get f b.

So I do almost the same to create >>=. After applying (a -> b) on a and getting b, do one more step and wrap it with f, so I return f b, hence I know I have a function (a -> f b).

Community
  • 1
  • 1
rapt
  • 10,528
  • 29
  • 92
  • 134
  • 10
    `Now when you think about it, f(a -> b) can be written as (a -> b)` This is not true, there is no function of type `Applicative f => f (a -> b) -> (a -> b)`. Simply put, if your theory where true, then by the curry howard isomorphism you could write the function to prove the theory - so write it (`forall f a b . Applicative f => f a -> (a -> f b) -> f b`), and see why it is actually impossible. Finally, even if you could construct a function of type `f a -> (a -> f b) -> f b` you still must prove it satisfies the Monad laws. – user2407038 Jan 21 '16 at 19:34
  • 2
    I would suggest you open up a editor, and try to implement `(>>=)` with just `` and `pure` and see if you can convince the compiler (and no this is not a proof but maybe you see where you get into trouble) – Carsten Jan 21 '16 at 19:47
  • @user2407038 `there is no function of type Applicative f => f (a -> b) -> (a -> b)` - what about `g(f x) = x`. If something depends on a, b, c, then it depends on a, b. – rapt Jan 21 '16 at 19:50
  • 3
    @rapt What is `g`? What is `f`? If you simply postulate these functions exists without proving it, then sure, you can prove absolutely anything. – user2407038 Jan 21 '16 at 19:53
  • @Carsten maybe you open up the answer editor here and try to correct my intuition? The signature of `` does not suffice, it comes with an algorithm how to use its two parameters in order to construct a result. Otherwise there are always dummy implementations that do very little. – rapt Jan 21 '16 at 19:58
  • 1
    you know that the `f` in `Applicative f` is not a function (in general) - think `f = Maybe` – Carsten Jan 21 '16 at 19:58
  • btw: because I just wanted you to try and see with the help of the compiler what goes wrong - it will not help if I claim to be the compiler (and I would do a bad job) ... but nevermind - good luck – Carsten Jan 21 '16 at 20:00
  • @Carsten `you know that the f in Applicative f is not a function` - if you read my whole post it's easy to see I have noticed it – rapt Jan 21 '16 at 20:08
  • 3
    @rapt I think you are confusing the value level and the type level. You seem to be describing functions at the value level but you are referring to type level things. The `f` here is a type constructor, parameterized by another type, not a value level function. – David Young Jan 21 '16 at 20:09
  • 1
    Your claim about `f(a -> b)` being able to be written as `(a -> b)` is kind of like saying that, for any function `f`, `f(x)` can be written as `x`. – David Young Jan 21 '16 at 20:11
  • @David Young No, I am aware of that, although I used `f` for the functor. Maybe you could specify exactly where I am confusing it. BTW constructor is a private case of a function, so... – rapt Jan 21 '16 at 20:13
  • 2
    @rapt Carsten's example is good: consider the concrete case of `f` being `Maybe`. How would you turn `Maybe (a -> b)` into `(a -> b)` in all cases? – David Young Jan 21 '16 at 20:15
  • 1
    I still think you have trouble seeing the difference between `Maybe` and `Just` here - to expand on @rapt s last comment: how to you get an `a -> b` out of `Nothing`? (and remember in your case you don't even know what `f` is) – Carsten Jan 21 '16 at 20:19
  • @David Young that's a good question. Have you read my intuitive algorithm above? I would ask myself how does `` use its two parameters? They can hardly be used together unless you do know to look inside the container type (f) and extract its internal goodies, or simply ignore them and provide a dummy implementation, or some other magic. – rapt Jan 21 '16 at 20:26
  • 2
    @rapt that's the job of the instance definition ... there of course you do know how to *peek* inside ... and that is exactly what you cannot do here (there is a nasty `forall f` ...) – Carsten Jan 21 '16 at 20:27
  • 1
    @rapt It looks like your algorithm uses the rewrite that I am claiming is invalid, so that would need to be addressed first. That rewrite is not correct because it must be true *for all* `f`, and if `f` is `Maybe` (for example) you cannot perform the rewrite. I would suggest trying to implement a function with the type `maybeRewrite :: Maybe (a -> b) -> (a -> b)`. – David Young Jan 21 '16 at 20:32
  • 3
    "if something is a function of x, y , z - then it's also a function of x, y" - Leaving aside that `f (a -> b)` is not "a function of f, a, and b", your claim is invalid. You need to *have* a z to get a function of x and y from a function of x, y, and z. – Ben Jan 21 '16 at 20:36
  • 3
    It's actually the other way around. If you need to provide a function `x -> y -> z -> c` (a function of x, y, and z, whose return I've dubbed c), and you have a function `x -> y -> c`, you can produce the needed function by writing a function that ignores `z` and calls the function you have. The reverse is impossible, without more information. – Ben Jan 21 '16 at 20:41
  • @Ben I did not say that in the general case I know to produce, I said I know it exists. But in the Functor/Applicative case I also think I can produce `>>=` from the input/output of Functor/Applicative functions. – rapt Jan 21 '16 at 21:02
  • the answer provides you with a fine argument why you cannot – Carsten Jan 21 '16 at 21:11
  • `g(f x) = x` is wrong because we can't pattern match on functions. functions are opaque. the definition `g (a ++ b) = ...` is illegal in Haskell. --- *"They can hardly be used together unless you do know to look inside the container type (f) and extract ..."* yes, when writing a concrete implementation for a concrete type. So your question is actually: **having a concrete implementation of Applicative instance for some concrete type, is it always possible to also implement Monad instance for that concrete type?** This is a valid question that was asked previously. – Will Ness Jan 21 '16 at 21:49
  • also, you see here how the container analogy breaks down. `Maybe` is not a container; it is a *possibly empty* container, of no more than one thing. You can't get a function out of a container that's empty. Another example is a list, which can contain arbitrary number of items. How do you get one item from among arbitrary many (possibly zero) items? – Will Ness Jan 21 '16 at 21:54

2 Answers2

12

Now when you look at it, f(a -> b) can be written as (a -> b)

No. It can't. Your intuition is (dangerously) far off at this point. That's like saying a hammer is perfect for driving screws in, since it already works for a nail*. You can't simply drop f here, it's part of the type**.

Instead, let's get the facts straight. An Applicative has three associated functions, counting Functor's fmap:

fmap  :: Functor f     =>   (a -> b) -> f a -> f b
pure  :: Applicative f =>                 a -> f a
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Here's another fact: you can define bind ((>>=)) in terms of join and vice versa:

join :: Monad m => m (m a) -> m a
join k = k >>= id

(>>=) :: Monad m => m a -> (a -> m b) -> m b
k >>= f = join (fmap f k)

are the implementations of join and bind you provided here part of the Monad definition, or are only join and bind signatures part of the Monad definition? [...] So now I ask myself why would they bother.

Those aren't the official definitions of course, since they would never terminate. You have to define (>>=) for your type if you want to make it a a monad:

instance Monad YourType where
   k >>= f = ...

Also, your join definition uses id which is not in the Monad interface, why is it mathematically legitimate?

First of all, id :: a -> a is defined for any type. Second, the mathematical definition of a monad is actually via join. So it's "more"*** legitimate. But most important of all, we can define the monad laws in terms of join (exercise).

If we created join via Applicative, we could also create bind. If we cannot create join via Applicative methods, neither can we derive bind. And join's type actually makes it obvious that we cannot derive it from Applicative:

join :: Monad m => m (m a) -> m a
             --    ^  ^       ^

Join is able to drop one of the m layers. Let's check whether it's possible to do the same in the other methods:

fmap  :: Functor f     =>   (a -> b) -> f a -> f b
                          ^                    ^
                        0 here              1 here
pure  :: Applicative f =>                 a -> f a
                                          ^  | ^
                                      0 here | 1 here
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
                          ^                    ^
                       1 here                1 here

The answer is no: none of the tools we're given by Applicative enables us collapse multiple m's into a single one. And that's also what is written in the Typeclassopedia right after the cited paragraph in the other question:

To see the increased power of Monad from a different point of view, let’s see what happens if we try to implement (>>=) in terms of fmap, pure, and (<*>). We are given a value x of type m a, and a function k of type a -> m b, so the only thing we can do is apply k to x. We can’t apply it directly, of course; we have to use fmap to lift it over the m. But what is the type of fmap k? Well, it’s m a -> m (m b). So after we apply it to x, we are left with something of type m (m b)—but now we are stuck; what we really want is an m b, but there’s no way to get there from here. We can add m’s using pure, but we have no way to collapse multiple m’s into one.

Note that join doesn't make it possible to get rid of m completely, that would be a total extraction, and—depending on some other functions—a feature of a comonad. Either way, make sure that you don't let your intuition go astray; trust and use the types.

* That comparison is a little bit bad, because you could actually try to dive a screw in with a hammer into a piece of wood. So think of a plastic screw, a rubber hammer and a carbon steel plate you want to drive the nail in. Good luck.

** Well, you can drop it, but then the type changes drastically.

*** Given that (>>=) and join are equivalent of power and any (>>=) using formula can be transformed to one only using join, they are of course both mathematical sound.

Community
  • 1
  • 1
Zeta
  • 95,453
  • 12
  • 173
  • 214
  • If you own a house, in which there is a hammer, then you own a hammer. – rapt Jan 21 '16 at 21:10
  • 1
    @rapt: In this case, you neither own the hammer, nor the house. You're allowed to use the hammer in the (indestructible) house to create your work, but not to take it (or your work) outside, unless the original owner gifts it to you. However, any monad simile doesn't quite cut it. – Zeta Jan 21 '16 at 21:12
  • @Carsten OP comes from Java/imperative languages. Given [this](https://stackoverflow.com/questions/34618366/haskell-infinite-lists-how-lazy-is-haskell) question, I guess that their first contact with Haskell was only recent. – Zeta Jan 21 '16 at 21:20
  • @Zeta, are the implementations of join and bind you provided here part of the Monad definition, or are only join and bind signatures part of the Monad definition? I am asking since I saw in some textbooks that they provided a definition based on the specific Monad (Maybe, Either, etc). So now I ask myself why would they bother. Also, your join definition uses id which is not in the Monad interface, why is it mathematically legitimate? – rapt Jan 21 '16 at 21:43
  • 4
    I think the root of your misunderstanding is this: An `f (a -> b)` is not actually an `a -> b` wrapped up in an `f`. It's not "a house in which there is a hammer". It's a thing that is *related* to `a -> b`, but it may or may not contain one. There are perfectly fine applicatives that sometimes don't contain a value of their type parameter, that contain many of them, that contain a way to produce them from other information they don't contain, or even that *never* contain such a value. You've got a house with a picture of a hammer on it, and the door is locked. – Ben Jan 21 '16 at 21:52
  • 1
    @rapt: I'm not Zeta, but: The implementations of `join` and `(>>=)` are *possible* implementations. Since they refer to each other, you'll have to define one of the two for your specific type (in Haskell, you have to define `(>>=)`), though. It's like defining `(-)` and `negate` as `a - b = a + negate b` and `negate a = 0 - b`. Either of those definitions is fine, so you only need to define how one of subtraction and negation works; but you do need to define at least one. – Antal Spector-Zabusky Jan 21 '16 at 22:12
  • I think a good example for this is `IO` - there is no number in `IO Int` - but it's a way to magically produce such an `Int` (possible different ones) – Carsten Jan 21 '16 at 22:12
  • 2
    @rapt: Also, it's ok to use `id` because it's already defined: `id :: a -> a`; `id x = x`. And since `id` can be applied at *any* type `a`, it's always ok to use it. – Antal Spector-Zabusky Jan 21 '16 at 22:12
10

Now when you look at it, f (a -> b) can be written as (a -> b)

Everyone has already contributed explaining that this is not a fact. Let me prove it to you.

If we genuinely had what you state then we should be able to write a function

expose :: f (a -> b) -> (a -> b)

Moreover, for any concrete data type we like, call it F, we ought to be able to write

expose_F :: F (a -> b) -> (a -> b)
expose_F = expose

Let's worry only about writing expose_F since if we can show that expose_F cannot be written for some F then we have surely shown that expose cannot be written.


Let me provide us a test F. It will certainly be non-intuitive feeling as I'm intending to use it to break intuition, but I'm happy to confirm all day long that there is nothing funny at all about

data F a = F

Indeed, it is a Functor

instance Functor F where
  fmap _ F = F

and an Applicative for that matter

instance Applicative F where
  pure _ = F
  F <*> F = F

even a Monad

instance Monad F where
  return _ = F
  F >>= _ = F

You can verify yourself that all of these typecheck. There's nothing wrong at all about F.

So what's just right about, F? Why did I choose it? Well F is interesting in that values of F a fail to contain anything related at all to a within them. Often people like to talk about data types (or Functors) as "containers" or "boxes". F forces us to remember that in a certain sense a box that's 0 inches deep is still a box. [0]

So surely we cannot write

expose_F :: F (a -> b) -> (a -> b)

There are a number of ways of proving this. The easiest is to appeal to my supposition that you, for instance, believe that there is no coerce function. But, if we had expose_F there would be!

coerce :: a -> b
coerce = expose_F F

More specifically, let me introduce another pathological type (which I again assure you is totally fine)

data Void

There are zero constructors of Void and we like to say therefore that Void has no members. It cannot be made to exist. But we can make one with expose_F.

void :: Void
void = expose_F F ()

In Haskell we're not technically sound enough to execute the above proofs. If you dislike the way I talk about impossibility then you can conjure up whatever types you like with a convenient infinite loop, casual call to

 error "Madness rides the star-wind... claws and teeth sharpened on centuries of corpses... dripping death astride a bacchanale of bats from nigh-black ruins of buried temples of Belial..."

or perhaps an unassuming undefined. But these are all on the path of madness.


There is no expose_F and therefore there is no expose.


[0] And to be completely clear, thinking of data types as boxes at all is often flawed. Instances of Functor tend to be "box-like", but here's another interesting data type which is difficult to think of as a box

data Unbox a = Unbox (a -> Bool)

unless perhaps you consider Unbox a to be a box containing a Bool and a negative a or something like that. Perhaps an IOU a.

J. Abrahamson
  • 64,404
  • 8
  • 128
  • 172