The related Question is
What is so special about Monads?
bind can be posed of fmap and join, so do we have to use monadic functions a -> m b?
In the first question:
What is so special about Monads?
A monad is a mathematical structure which is heavily used in (pure) functional programming, basically Haskell. However, there are many other mathematical structures available, like for example applicative functors, strong monads, or monoids. Some have more specific, some are more generic. Yet, monads are much more popular. Why is that?
The ment to reply the question:
As far as I recall, monads were popularised by Wadler, and at the time the idea of doing IO without tedious CPS and parsing without explicit state passing were huge selling points; it was a hugely exciting time. A.F.A.I.R., Haskell didn't do constructor classes, but Gofer (father of Hugs) did. Wadler proposed overloading list prehension for monads, so the do notation came later. Once IO was monadic, monads became a big deal for beginners, cementing them as a major thing to grok. Applicatives are much nicer when you can, and Arrows more general, but they came later, and IO sells monads hard. – AndrewC May 9 '13 at 1:34
The answer by @Conal is:
I suspect that the disproportionately large attention given to this one particular type class (
Monad
) over the many others is mainly a historical fluke. People often associateIO
withMonad
, although the two are independently useful ideas (as are list reversal and bananas). BecauseIO
is magical (having an implementation but no denotation) andMonad
is often associated withIO
, it's easy to fall into magical thinking aboutMonad
.
First of all, I agree with them, and I think the usefulness of Monads mostly arises from Functors that we can embed many functions within the structure, and Monads is a little expansion for robustness of function position by join
: M(M(X)) -> M(X)
to avoid the nested type.
In the 2nd Question:
do we have to use monadic functions a -> m b?
so many tutorials around the web still insist to use a monadic functions since that is the Kleisli triple and the monad-laws.
and many answers like
I like to think of such an m as meaning "plan-to-get", where "plans" involve some sort of additional interaction beyond pure putation.
or
In situations where
Monad
isn't necessary, it is often simpler to useApplicative
,Functor
, or just basic pure functions. In these cases, these things should be (and generally are) used in place of aMonad
. For example:
ws <- getLine >>= return . words -- Monad
ws <- words <$> getLine -- Functor (much nicer)
To be clear: If it's possible without a monad, and it's simpler and more readable without a monad, then you should do it without a monad! If a monad makes the code more plex or confusing than it needs to be, don't use a monad! Haskell has monads for the sole purpose of making certain plex putations simpler, easier to read, and easier to reason about. If that's not happening, you shouldn't be using a monad.
Reading their answers, I suppose their special feeling about Monad arises from the historical incident that Haskell munity has happend to chose Monads in Kleisli category to solve their problem(IO etc.)
So, again, I think the usefulness of Monads mostly arises from Functors that we can embed many functions within the structure, and Monads is a little expansion for robustness of function position by join
: M(M(X)) -> M(X)
to avoid the nested type.
In fact, in JavaScript I implemented as below..
Functor
console.log("Functor");
{
const unit = (val) => ({
// contextValue: () => val,
fmap: (f) => unit((() => {
//you can do pretty much anything here
const newVal = f(val);
// console.log(newVal); //IO in the functional context
return newVal;
})()),
});
const a = unit(3)
.fmap(x => x * 2) //6
.fmap(x => x + 1); //7
}
The related Question is
What is so special about Monads?
bind can be posed of fmap and join, so do we have to use monadic functions a -> m b?
In the first question:
What is so special about Monads?
A monad is a mathematical structure which is heavily used in (pure) functional programming, basically Haskell. However, there are many other mathematical structures available, like for example applicative functors, strong monads, or monoids. Some have more specific, some are more generic. Yet, monads are much more popular. Why is that?
The ment to reply the question:
As far as I recall, monads were popularised by Wadler, and at the time the idea of doing IO without tedious CPS and parsing without explicit state passing were huge selling points; it was a hugely exciting time. A.F.A.I.R., Haskell didn't do constructor classes, but Gofer (father of Hugs) did. Wadler proposed overloading list prehension for monads, so the do notation came later. Once IO was monadic, monads became a big deal for beginners, cementing them as a major thing to grok. Applicatives are much nicer when you can, and Arrows more general, but they came later, and IO sells monads hard. – AndrewC May 9 '13 at 1:34
The answer by @Conal is:
I suspect that the disproportionately large attention given to this one particular type class (
Monad
) over the many others is mainly a historical fluke. People often associateIO
withMonad
, although the two are independently useful ideas (as are list reversal and bananas). BecauseIO
is magical (having an implementation but no denotation) andMonad
is often associated withIO
, it's easy to fall into magical thinking aboutMonad
.
First of all, I agree with them, and I think the usefulness of Monads mostly arises from Functors that we can embed many functions within the structure, and Monads is a little expansion for robustness of function position by join
: M(M(X)) -> M(X)
to avoid the nested type.
In the 2nd Question:
do we have to use monadic functions a -> m b?
so many tutorials around the web still insist to use a monadic functions since that is the Kleisli triple and the monad-laws.
and many answers like
I like to think of such an m as meaning "plan-to-get", where "plans" involve some sort of additional interaction beyond pure putation.
or
In situations where
Monad
isn't necessary, it is often simpler to useApplicative
,Functor
, or just basic pure functions. In these cases, these things should be (and generally are) used in place of aMonad
. For example:
ws <- getLine >>= return . words -- Monad
ws <- words <$> getLine -- Functor (much nicer)
To be clear: If it's possible without a monad, and it's simpler and more readable without a monad, then you should do it without a monad! If a monad makes the code more plex or confusing than it needs to be, don't use a monad! Haskell has monads for the sole purpose of making certain plex putations simpler, easier to read, and easier to reason about. If that's not happening, you shouldn't be using a monad.
Reading their answers, I suppose their special feeling about Monad arises from the historical incident that Haskell munity has happend to chose Monads in Kleisli category to solve their problem(IO etc.)
So, again, I think the usefulness of Monads mostly arises from Functors that we can embed many functions within the structure, and Monads is a little expansion for robustness of function position by join
: M(M(X)) -> M(X)
to avoid the nested type.
In fact, in JavaScript I implemented as below..
Functor
console.log("Functor");
{
const unit = (val) => ({
// contextValue: () => val,
fmap: (f) => unit((() => {
//you can do pretty much anything here
const newVal = f(val);
// console.log(newVal); //IO in the functional context
return newVal;
})()),
});
const a = unit(3)
.fmap(x => x * 2) //6
.fmap(x => x + 1); //7
}
The point is we can implement whatever we like in the Functor structure, and in this case, I simply made it IO/console.log
the value.
Another point is, to do this Monads is absolutely unnecessary.
Monad
Now, based on the Functor implementation above, I add extra join: MMX => MX
feature to avoid the nested structure that should be helpful for robustness of plex functional position.
The functionality is exactly identical to the Functor above, and please note the usage is also identical to the Functor fmap
. This does not require a "monadic function" to bind
(Kleisli position of monads).
console.log("Monad");
{
const unit = (val) => ({
contextValue: () => val,
bind: (f) => {
//fmap value operation
const result = (() => {
//you can do pretty much anything here
const newVal = f(val);
console.log(newVal);
return newVal;
})();
//join: MMX => MX
return (result.contextValue !== undefined)//result is MX
? result //return MX
: unit(result) //result is X, so re-wrap and return MX
}
});
//the usage is identical to the Functor fmap.
const a = unit(3)
.bind(x => x * 2) //6
.bind(x => x + 1); //7
}
Monad Laws
Just in case, this implementation of the Monad satisfies the monad laws, and the Functor above does not.
console.log("Monad laws");
{
const unit = (val) => ({
contextValue: () => val,
bind: (f) => {
//fmap value operation
const result = (() => {
//you can do pretty much anything here
const newVal = f(val);
//console.log(newVal);
return newVal;
})();
//join: MMX => MX
return (result.contextValue !== undefined)
? result
: unit(result)
}
});
const M = unit;
const a = 1;
const f = a => (a * 2);
const g = a => (a + 1);
const log = m => console.log(m.contextValue()) && m;
log(
M(f(a))//==m , and f is not monadic
);//2
console.log("Left Identity");
log(
M(a).bind(f)
);//2
console.log("Right Identity");
log(
M(f(a))//m
.bind(M)// m.bind(M)
);//2
console.log("Associativity");
log(
M(5).bind(f).bind(g)
);//11
log(
M(5).bind(x => M(x).bind(f).bind(g))
);//11
}
So, here is my question.
I may be wrong.
Is there any counter example that Functors cannnot do what Monads can do except the robustness of functional position 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.
Thanks.
edit(2018-11-01)
Reading the answers, I agree it's not appropriate to perform console.log
inside the IdentityFunctor that should satisfy Functor-laws, so I mented out like the Monad code.
So, eliminating that problem, my question still holds:
Is there any counter example that Functors cannnot do what Monads can do except the robustness of functional position 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 from @DarthFennec 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 likejoin
just strips the outer type, but the monad's value is unchanged.
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)
monad (in puter science): Relation to monads in category theory
and that's exactly what my code does.
On the other hand,
This is not the case.
join
is the heart of a monad, and it's what allows the monad to do things.
I know many people implements 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,
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 bine outer values in a consistent way. This is much more powerful than a simple functor.
These observation does not fit the fact of the existence of Maybe functor and Free monad.
Share edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Nov 1, 2018 at 21:53 user10583507user10583507 9- I don't get your question. Yes, flattening nested structures is exactly what Monads can do that Functors cannot. That's why we use two different terms for two different concepts. – Bergi Commented Nov 1, 2018 at 22:40
-
Branching on
result.contextValue !== undefined
makes no sense. You should not overloadbind
to dofmap
as well. Also, I don't get what point you are trying to make with that example? – Bergi Commented Nov 1, 2018 at 22:48 - @Bergi I wrote " Is there any counter example that Functors cannnot do what Monads can do except the robustness of functional position by flattening the nested structure?" and other got the idea.Sorry, but your ment is not helpful. – user10583507 Commented Nov 1, 2018 at 23:04
- 3 You must be the same person who's kept asking questions just like this on several different accounts over the last week or so. Why do you keep creating new accounts? – Carl Commented Nov 1, 2018 at 23:13
- I don't use several accounts to ask. – user10583507 Commented Nov 1, 2018 at 23:18
4 Answers
Reset to default 6Is there any counter example that Functors cannnot do what Monads can do except the robustness of functional position by flattening the nested structure?
I think this is the important point:
Monads is a little expansion for robustness of function position by join :
M(M(X)) -> M(X)
to avoid the nested type.
"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. This is not the case. join
is the heart of a monad, and it's what allows the monad to do things.
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 bine outer values in a consistent way. This is much more powerful than a simple functor.
The point is we can implement whatever we like in the Functor structure, and in this case, I simply made it IO/
console.log
the value.
This is incorrect, actually. The only reason you were able to do IO here is because you're using Javascript, and you can do IO anywhere. In a purely functional language like Haskell, IO cannot be done in a functor like this.
This is a gross generalization, but for the most part it's useful to describe IO
as a glorified State
monad. Each IO
action takes an extra hidden parameter called RealWorld
(which represents the state of the real world), maybe reads from it or modifies it, and then sends it on to the next IO
action. This RealWorld
parameter is threaded through the chain. If something is written to the screen, that's RealWorld
being copied, modified, and passed along. But how does the "passing along" work? The answer is join
.
Say we want to read a line from the user, and print it back to the screen:
getLine :: IO String
putStrLn :: String -> IO ()
main :: IO ()
main = -- ?
Let's assume IO
is a functor. How do we implement this?
main :: IO (IO ())
main = fmap putStrLn getLine
Here we've lifted putStrLn
to IO
, to get fmap putStrLn :: IO String -> IO (IO ())
. If you recall, putStrLn
takes a String
and a hidden RealWorld
and returns a modified RealWorld
, where the String
parameter is printed to the screen. We've lifted this function with fmap
, so that it now takes an IO
(which is an action that takes a hidden RealWorld
, and returns a modified RealWorld
and a String
), and returns the same io action, just wrapped around a different value (a pletely separate action that also takes a separate hidden RealWorld
and returns a RealWorld
). Even after applying getLine
to this function, nothing actually happens or gets printed.
We now have a main :: IO (IO ())
. This is an action that takes a hidden RealWorld
, and returns a modified RealWorld
and a separate action. This second action takes a different RealWorld
and returns another modified RealWorld
. This on its own is pointless, it doesn't get you anything and it doesn't print anything to the screen. What needs to happen is, the two IO
actions need to be connected together, so that one action's returned RealWorld
gets fed in as the other action's RealWorld
parameter. This way it bees one continuous chain of RealWorld
s that mutate as time goes on. This "connection" or "chaining" happens when the two IO
actions are merged with join
.
Of course, join
does different things depending on which monad you're working with, but for IO
and State
-type monads, this is more or less what's happening under the hood. There are plenty of situations where you're doing something very simple that doesn't require join
, and in those cases it's easy to treat the monad as a functor or applicative functor. But usually that isn't enough, and in those cases we use monads.
EDIT: Responses to ments and edited question:
I don't see any definition of Monads in categort theory explains this. All I read about join is stil
MMX => MX
and that is exactly what my code does.
Can you also tell exactly what a function String -> String
does? Might it not return the input verbatim, reverse it, filter it, append to it, ignore it and return a pletely different value, or anything else that results in a String
? A type does not determine what a function does, it restricts what a function can do. Since join
in general is only defined by its type, any particular monad can do anything allowed by that type. This might just be stripping the outer layer, or it might be some extremely plex method of bining the two layers into one. As long as you start with two layers and end up with one layer, it doesn't matter. The type allows for a number of possibilities, which is part of what makes monads so powerful to begin with.
There is MaybeFunctor in Haskell. There's no "join" or "bind" there, and I wonder from where the power e. What is the difference between MaybeFunctor and MaybeMonad?
Every monad is also a functor: a monad is nothing more than a functor that also has a join
function. If you use join
or bind
with a Maybe
, you're using it as a monad, and it has the full power of a monad. If you do not use join
or bind
, but only use fmap
and pure
, you're using it as a functor, and it bees limited to doing the things a functor can do. If there's no join
or bind
, there is no extra monad power.
I believe "Avoiding the nested type" is not just a neat side-effect, but a definition of "join" of Monad in category theory
The definition of join
is a transformation from a nested monad to a non-nested monad. Again, this could imply anything. Saying the purpose of join
is "to avoid the nested type" is like saying the purpose of +
is to avoid pairs of numbers. Most operations bine things in some way, but very few of those operations exist simply for the sake of having a bination of things. The important thing is how the bining happens.
there is Maybe functor in Haskell, that does not has
join
, or there is Free monad thatjoin
is embedded from the first place into the defined structure. They are objects that users define Functors to do things.
I've already discussed Maybe
, and how when you use it as a functor only, it can't do the things it can do if you use it as a monad. 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 bine 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 bined 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 theMaybe
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.
The point is we can implement whatever we like in the Functor structure, and in this case, I simply made it IO/
console.log
the value.Another point is, to do this Monads is absolutely unnecessary.
The problem is that once you do that your functor is no longer a functor. Functors should preserve identities and position. For Haskell Functor
s, those requirements amount to:
fmap id = id
fmap (g . f) = fmap g . fmap f
Those laws are a guarantee that all fmap
does is using the supplied function to modify values -- it doesn't do funny stuff behind your back. In the case of your code, fmap(x => x)
should do nothing; instead, it prints to the console.
Note that all of the above applies to the IO
functor: if a
is an IO
action, executing fmap f a
will have no I/O effects other than those a
already had. One stab at writing something similar in spirit to your code might be...
applyAndPrint :: Show b => (a -> b) -> a -> IO b
applyAndPrint f x = let y = f x in fmap (const y) (print y)
pseudoFmap :: Show b => (a -> b) -> IO a -> IO b
pseudoFmap f a = a >>= applyAndPrint f
... but that makes use of Monad
already, as we have an effect (printing a result) which depends on the result of a previous putation.
It goes without saying that if you are so inclined (and your type system allows it) you can write code that disregards all of those distinctions. There is, however, a trade-off: the decreased power of Functor
with respect to Monad
es with extra guarantees on what functions using the interface can and cannot do -- that is what makes the distinctions useful in the first place.
Your “functor” is very manifestly not a functor, violating both the identity and position law:
console.log("Functor");
{
const unit = (val) => ({
// contextValue: () => val,
fmap: (f) => unit((() => {
//you can do pretty much anything here
const newVal = f(val);
console.log(newVal); //IO in the functional context
return newVal;
})()),
});
console.log("fmap(id) ...");
const a0 = unit(3)
.fmap(x => x); // prints something
console.log(" ≡ id?");
const a1 = (x => x)(unit(3)); // prints nothing
console.log("fmap(f) ∘ fmap(g) ...");
const b0 = unit(3)
.fmap(x => 3*x)
.fmap(x => 4+x); // prints twice
console.log(" ≡ fmap(f∘g)?");
const b1 = unit(3)
.fmap(x => 4+(3*x)); // prints once
}
overly long ment:
I would suggest forgetting about Kleisli categories for now; I don't believe they have anything to do with your confusion.
Also while I still don't fully understand your question and assertions, some context that might be useful: category theory is extremely general and abstract; the concepts like Monad
and Functor
as they exist in haskell are (necessarily) somewhat less general and less abstract (e.g. the notion of the category of "Hask").
As a general rule the more concrete (the less abstract) a thing bees, the more power you have: if I tell you you have a vehicle then you know you have a thing that can take you from one place to another, but you don't know how fast, you don't know whether it can go on land, etc. If I tell you you have a speed boat then there opens up a whole larger world of things you can do and reason about (you can use it to catch fish, you know that it won't get you from NYC to Denver).
When you say:
What's so special about Monads in Kleisli category?
...I believe you're making the mistake of suspecting that the conception of Monad
and Functor
in haskell is in some way more restrictive relative to category theory but, as I try to explain by analogy above, the opposite is true.
Your code is the same sort of flawed thinking: you model a speedboat (which is a vehicle) and claim it shows that all vehicles are fast and travel on water.