0
$\begingroup$

There is a controversy about Monad implementation in S.O .

The original question is,

What's so special about Monads in Kleisli category?

Is there any counterexample that Functors cannot do what Monads can do except the robustness of functional composition by flattening the nested structure?

What's so special about Monads in Kleisli category? It seems like it's fairly possible to implement Monads with a little expansion to avoid the nested structure of Functor and without the monadic functions a -> m b that is the entity in Kleisli category.

An answer is

"Avoiding the nested type" is not, in fact, the purpose of join, it's just a neat side-effect. The way you put it makes it sound like join just strips the outer type, but the monad's value is unchanged.

You can think of a functor as basically a container. There's an arbitrary inner type, and around it an outer structure that allows some variance, some extra values to "decorate" your inner value. fmap allows you to work on the things inside the container, the way you would work on them normally. This is basically the limit of what you can do with a functor.

A monad is a functor with a special power: where fmap allows you to work on an inner value, bind allows you to combine outer values in a consistent way. This is much more powerful than a simple functor.

join is the heart of the monad because it encompasses everything a monad can do that a functor cannot. Since join and >>= are isomorphic and you can define each in terms of the other, defining >>= without join still provides the same degree of freedom (you could say that defining >>= indirectly defines join, and vice versa). You get the same thing either way, except that >>= is more convenient and join is more "pure". So it is really a boring question to compare the two. – DarthFennec

The objection by the questioner is,

I believe "Avoiding the nested type" is not just a neat side-effect, but a definition of "join" of Monad in category theory,

the multiplication natural transformation μ:T∘T⇒T of the monad provides for each object X a morphism μX:T(T(X))→T(X)

and that's exactly what my code does.

I know many people implement monads in Haskell in this manner, but the fact is, there is Maybe functor in Haskell, that does not has join, or there is Free monad that join is embedded from the first place into the defined structure. They are objects that users define Functors to do things.

Therefore, ... [your] observation does not fit the fact of the existence of Maybe functor and Free monad.

Which opinion is correct?

PS. Additional comment from DarthFennec (who answers) as below:


Free is weird, in that it's one of the few monads that doesn't actually do anything.

Free can be used to turn any functor into a monad, which allows you to use do notation and other conveniences. However, the conceit of Free is that join does not combine your actions the way other monads do, instead it keeps them separate, inserting them into a list-like structure; the idea being that this structure is later processed and the actions are combined by separate code. An equivalent approach would be to move that processing code into join itself, but that would turn the functor into a monad and there would be no point in using Free. So the only reason Free works is because it delegates the actual "doing things" part of the monad elsewhere; its join opts to defer action to code running outside the monad. This is like a + operator that, instead of adding the numbers, returns an abstract syntax tree; one could then process that tree later in whatever way is needed.

These observation does not fit the fact of the existence of Maybe functor and Free monad.

You are incorrect. As explained, Maybe and Free fit perfectly into my previous observations:

  • The Maybe functor simply does not have the same expressiveness as the Maybe monad.
  • The Free monad transforms functors into monads in the only way it possibly can: by not implementing a monadic behavior, and instead simply deferring it to some assumed processing code.

First of all the word he uses "Free is weird" or "the conceit of Free" unduly disparaging the Free monad, and this words does not justify what he insists obviously.

An equivalent approach would be to move that processing code into join itself, but that would turn the functor into a monad and there would be no point in using Free.

My question would be if "join is the heart of the monad", how come the " processing code" has been moved away from the heart =join and move-back into join again? That is the weird.

Free is one of the generalization to abstract the "processing code" of Monad. Monad structure including join that satisfies the monad laws are pre-defined without the "processing code" that would be functor/ or function if it's Operational monad.

So the only reason Free works is because it delegates the actual "doing things" part of the monad elsewhere; its join opts to defer action to code running outside the monad.

In either way, again, "doing things" does not have to locate in join. If someone strongly insists "join is the heart of the monad", maybe it's ok to do so. However, as Free-monad, it's totally reasonable a user let join be pre-defined in a generalized structure of Monad as a functionality of flattening the structure, or other monads also does not have to move "doing things" to join because it's not the heart of the monad anyway.

$\endgroup$

closed as unclear what you're asking by David Richerby, Evil, Derek Elkins, D.W. Nov 6 '18 at 21:23

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 4
    $\begingroup$ What's your question? This mostly seems to be a discussion/argument copy-pasted from somewhere else on Stack Exchange. $\endgroup$ – David Richerby Nov 2 '18 at 22:40
  • 2
    $\begingroup$ I would second @DavidRicherby. "Which opinion is correct?" sounds like asking for more opinions. (Disclaimer, I cannot say I am familiar with category-theory and functional-programming) $\endgroup$ – Apass.Jack Nov 3 '18 at 6:01
  • $\begingroup$ This is a site for technical questions, not for debate or discussion. As such, we require you to articulate a specific technical question that you want an answer to. Subjective questions or questions calling for opinion or discussion are off-topic here. "Which opinion is correct?" does not seem like a suitable question. And "the heart of" sounds like a matter of opinion. Do you have a specific technical question, that has an objectively correct answer? I encourage you to reflect on the discussion, see if you can find what are the facts that you want to learn, and ask about that. $\endgroup$ – D.W. Nov 6 '18 at 21:25
  • $\begingroup$ You'll probably need to pare the question down, too. That's a massive wall of text. Finally, a question here is not the place to tell others that they are incorrect or respond to quotes or statements from others. $\endgroup$ – D.W. Nov 6 '18 at 21:35
3
$\begingroup$

I'm not sure I fully understand the question, but since it's my answer you're wondering about, I'll do my best to clarify my meaning.1

join is the heart of the monad because it encompasses everything a monad can do that a functor cannot. Is this true?

Let's find out!

We can define a functor2 like so:

class Functor' f where
    pure' :: a -> f a
    fmap' :: (a -> b) -> f a -> f b
    ap'   :: f (a -> b) -> f a -> f b

We can define a monad in the following way:

class Monad' m where
    pure' :: a -> m a
    fmap' :: (a -> b) -> m a -> m b
    ap'   :: m (a -> b) -> m a -> m b
    join' :: m (m a) -> m a

As you can see, these are the same except for the added join' on the Monad' class. Since without this join' the Monad' is indistinguishable from the Functor', I think it's safe to say that join' embodies everything in Monad' that is not in Functor'.

This can also be seen in the way the Haskell Prelude defines Monad:

class Applicative m => Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b

    (>>)   :: m a -> m b -> m b
    m >> k = m >>= (\_ -> k)

    return :: a -> m a
    return = pure

From this we can see the following:

  • Any Monad m must also be an Applicative m, meaning that all Monads are also Functors.
  • >> is simply a convenience method, and is defined in terms of >>=.
  • return is just another name for the applicative pure.
  • The only remaining method defined by Monad is >>=. Therefore, >>= is the one thing Monad has that Applicative doesn't have.

However, we can define >>= in the following way:

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

fmap is part of Functor, so join is the only remaining thing that's unique to Monad. This is why I described join as "the heart of the monad".

In either way, again, "doing things" does not have to locate in join. If someone strongly insists "join is the heart of the monad", maybe it's ok to do so. However, as Free-monad, it's totally reasonable a user let join be pre-defined in a generalized structure of Monad as a functionality of flattening the structure, or other monads also does not have to move "doing things" to join because it's not the heart of the monad anyway.

I'm not sure I understand what you mean. join is the place in a Monad where the actual "doing" is defined; this is where the purpose of the Monad is encoded. The purpose of Free is to displace the "actual" processing logic for the user to deal with somewhere else. I suppose you could say that Free is a way to transform a Monad problem into a data processing problem, in that join creates a data structure that can later be processed. This is unusual because most Monads encode destructive processing into join, but this doesn't have to be the case. But the fact that the "heart" of Free is the displacement of the business logic does not mean join doesn't represent the "heart" of a Monad.


1Note that I don't have a category theory background, so I'm going to be working in the context of Haskell. I assume that's okay, since this question is tagged as [functional-programming].

2This is actually an "applicative functor", which defines a couple of extra methods. I believe this is appropriate, as the questioner's original question defined a functor with fmap and unit, the latter being another name for the applicative pure.

$\endgroup$
  • $\begingroup$ Comments are not for extended discussion; this conversation has been moved to chat. $\endgroup$ – D.W. Nov 6 '18 at 21:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.