
If monads are the solution, what is the problem? - theaeolist
http://danghica.blogspot.com/2018/07/haskell-if-monads-are-solution-what-is.html
======
lmm
> Monads in Haskell are used as a mechanism for scheduling evaluation, thus
> turning a language with a hard-to-predict evaluation strategy into a
> language with predictable, sequential, interactions. This makes it possible
> to add interactive computations such as state and input-output to the
> language, noting that benign (non-interactive) computations are already a
> part of the language, transparent to the type system.

That's a very operational/imperative perspective, and to my mind is putting
the cart before the horse. To me the whole value of monads is that they let
you talk about things like state and input-output in a declarative way, using
plain old functions and values, rather than having to use these
awkward/confusing concepts of "evaluation" and "scheduling".

~~~
alipang
There is a point to it though - continuations, for instance, have monadic
structure - but at the cost of introducing a strict evaluation order. There is
other work related to the linear logic that does what Monads do - provide an
abstract syntax - but working better with laziness, so it's not exactly wrong
to give Monads an operational perspective.

There are many perspectives one can use to understand Monads. The operational
one is valid - and useful for many beginners. Of course, it's not the only way
to look at them, as you point out.

~~~
a-saleh
What is this linear-logic thing that does what monads do? I haven't yet
grokked linear-logic (apart from the advert, that you might want to use it to
reason about resource constraints), so I would like to read about this :)

~~~
drb91
I think the commenter is referring to the process of declaring an order for
executing a compute graph. With CSP languages this is established by the order
in which the statements appear and reference each other, and computation is
explicit: all previous statements in a given process have finished executing
at any given point.

This is in contrast to Haskell that uses monads to declare the computation
graph (well, functions, but monads are under discussion here). In this world,
data is computed lazily as needed. The questions you ask about the code center
around functions and their dependencies, not necessarily computation order.

A task that is trivial to express with on paradigm might be non-trivial to
express on the other: haskell is excellent about expressing lazy computation
and side effects, whereas a CSP language will offer easy reasoning about the
specific and correct order of execution.

Note I am not a PL expert, just an enthusiast. My diction may be off for the
domain.

------
foldr
The more I've learned about Haskell, the more I've come to realize that
Haskell's approach to IO really has very little to do with Monads.
Fundamentally, Haskell implementations sequence IO actions using hidden magic.
(GHC uses dummy world variables.) As it happens, the operations for
constructing IO actions from pure values and for sequencing IO actions form a
Monad. But that's not really any more interesting than the fact that e.g. (\x
-> [x]) and concatMap form a Monad.

~~~
mbid
You're describing my pet peeve with research in programming languages, or more
generally use of abstractions in CS. What you're writing makes sense:
Understand the structure of your problem, then think of similar problems
you've seen before, finally try to abstract away details that are irrelevant
for the solutions in each case. If you're lucky, all of these problems are an
instance of one general one, and need to be solved only once. In your example,
it's that IO and [] are both monads and thus share some structure.

What's often happening in PL theory, though, is that people start off with
axioms/syntax and then mutilate the actual problem until it fits the syntax
(still badly). That's how you get ridiculous stuff like "everything is an
object" or "everything is a file". In PL theory, people will usually first
make up some syntax/theory and then search for models. If physicists worked
the same way, they'd write down random formulae and would then go out to find
phenomena described by them. You'd probably read this comment on a cave wall.

~~~
JoshCole
Axiomatic formulations of a problem _do not_ have the same goals as
abstractions. They can appear similar on the surface, but one has to do with
the laying of a foundation and the other is about problem simplification. One
is about allowing great leaps in understanding after a great deal of
derivation, the other about the reduction of work via problem equivalence.
Criticizing someone for using an axiomatic formulation rather than a proper
abstraction in programming language theory is like criticizing an architect
for using pencil in laying his foundation on paper rather the much more
appropriate cement at the construction site.

You don't get everything is an X because of axiomatic formulation. You get it
for the same sort of reason that instead of spending ages describing the
configurations of atoms in front of you, you throw all that out and call it a
screen. Is everything still atoms? Yes! But we can call it a screen and that
makes things massively easier to reason about and so we do it, because we like
to reason well. But there are still benefits to thinking the other way,
thinking from the very foundations. They are just different benefits, which is
why we choose to think of problems from more than one perspective.

~~~
mbid
I didn't mean to critize all axiomatization but the concrete axiomatizations
arrived at by PL research. In my opinion, one should start with a thorough
understanding of the problem domain, and then try to come up with a syntax
that allows expression of your intuition as faithfully as possible. Form
follows function. Instead, I feel like PL research is often too focused on the
concrete syntax considered, thus function follows form. Case in point:
Building models for the syntax at hand is often an afterthought, done only to
prove consistency. I'd argue that one should start with as precise an
understanding of the intended models as possible, and then come with up with
an axiomatization (thus, syntax) for the intended semantics.

For example, the notion of elementary topos has been invented because its
creators wanted to capture the way Grothendieck toposes kind of behave like
constructive sets. This I find very useful, and also the axiomatizations of
elementary toposes. On the other hand, Martin-Löf type theory didn't have a
formal semantics at first, then an erroneous one, and finally ~20 years later
a kind of acceptable one. And its category of models is... not really
interesting. Except for categories of assemblies, I don't know of a single
model of ML type theory that's not also an elementary topos. And the
interesting thing about assemblies is that they can be turned into objects of
the associated topos... so yeah.

~~~
joel_ms
And yet MLTT led to Coq, Agda, Idris and Lean, while your ”PL practice”
approach sounds like it would lead to, well, Go.

------
agentultra
If you're looking to understand what Monads are or _why_ they're interesting
this isn't the place to figure that out.

Monad: if you have a data structure that wraps some value of any type, then
you also have to implement the interface that provides `return` and `bind`
(there's talk about requiring another method, `join` but I digress). The data
structure is the "context" and the interface is how you compose values from
other Monads together using functions. If you can prove your implementation of
the interface methods follow certain rules then you have a Monad.

Why is this pattern/abstraction/interface useful in Haskell?

I think there are several reasons. The article touches one of the most talked
about: sequencing. Because of the way the rules of the Monad interface are
structured you're guaranteed that the functions you use to compose your Monads
together will be executed in sequence with respect to their definition in your
function body.

(How it all ties together is straight-forward but requires a certain pedagogy
and exposition to follow that I won't get into.)

This is, consequently, what makes Haskell the best imperative language in the
world. Not only do you get to use it's wonderful type system but you can
compose together small imperative programs into bigger, more useful ones.

~~~
stcredzero
_Monad: if you have a data structure that wraps some value of any type, then
you also have to implement the interface that provides `return` and `bind`_

So it's a wrapper that implements a particular interface? Then why, oh why, do
we have so many people who spout, "A monad is just a monoid in the category of
endofunctors, what's the problem?" Gilad Bracha covered this in a talk once.

 _Because of the way the rules of the Monad interface are structured you 're
guaranteed that the functions you use to compose your Monads together will be
executed in sequence with respect to their definition in your function body._

So it's a way of imperatively declaring execution sequence dependencies that
lets you reason as if there's only pure functions most of the time. This seems
to be the case for all functional programming languages/environments. When you
get down to it, they're all, in the words of Garrison Keillor, "Pure, mostly."

~~~
bitwize
> Then why, oh why, do we have so many people who spout, "A monad is just a
> monoid in the category of endofunctors, what's the problem?"

It's a joke/dank maymay that Haskells still find funny long after it's pissed
everyone else off.

The idea is that since category theory is described as "generalized abstract
nonsense" _by its practitioners_ , describing monads in terms of their strict
category-theoretical definition helps no one, especially those new to Haskell.
The problem is that unless your audience _is_ steeped in category theory, they
can't appreciate the irony of the situation and you just come off sounding
like a dick.

~~~
stcredzero
_The problem is that unless your audience is steeped in category theory, they
can 't appreciate the irony of the situation_

Who needs mindshare, when you can have in-jokes instead? Better to be the one
everyone initially laughs at, but wind up with all the mindshare, than to be
the one doing all the laughing who gets left behind.

~~~
derefr
It's not quite an in-joke. It's in-gallows humour: something common to all
transmission of hard-to-communicate knowledge.

Anyone on the far side of a gulf of enlightenment—and understanding monads
_is_ an enlightenment, if a small one—knows that it's effectively impossible
to actually communicate the particular thing they learned that helped them
achieve enlightenment. (Well, it's not impossible to communicate what _they_
learned; it's just that that's roughly useless to anyone else. An
enlightenment is a realization that culminates only from _all_ of a set of
micro-skills being attained; and each person is missing different micro-skills
to start with. You can look back and see the micro-skills _you_ learned, and
teach those, but you have no idea what micro-skills you started off already in
possession of that others did not—what micro-skills you take for granted—and
so you _cannot_ teach those.)

When faced with someone starting on the path to an enlightenment, who asks you
to simply summarize the path for them, there's no way to actually _usefully_
tell them. But it's kind of rude to just say nothing—and on a forum like this,
equally rude to assume a role of a master verbally lashing an apprentice (ala
a Zen parable) for assuming they could understand the concept without working
their brain up through all the micro-skills it was missing first.

So, what you do instead is, you make a joke. A joke that seems opaque at the
time, but which the learner, having journeyed further down the path, will
realize _was_ the truth they sought, and exactly what they asked for—it was
just a truth that was, necessarily, completely useless to them at the time.
Any such truth would be.

You don't make the joke to be snarky. You make the joke because you wish that,
this time, it would work, and the learner would skip the path and achieve the
enlightenment; that you could just reach across the gulf and bring them over.
But you know it won't. The punchline of the "joke" is not played on the
learner, but on the teacher: it is that the world is cruel and to teach skills
that require enlightenments is to suffer knowing your students lay across this
gulf, and most of what you say will miss them entirely because of the mismatch
in micro-skill acquisition between you.

(Though, I suppose, when the learner attempts to teach the concept themselves,
and realizes the only thing they want to say is the same useless thing that
was said to them—this would be the joke "paying off." That pay-off makes it
sort of like a traditional joke, in the sense that choosing to "tell" it
rather than simply thinking it to oneself is choosing to set the learner up
for that later pay-off.)

~~~
stcredzero
_An enlightenment is a realization that culminates only from all of a set of
micro-skills being attained_

There are lots of disciplines/areas of human knowledge and culture where most
of the micro-skills have inherent rewards for learning them.

 _When faced with someone starting on the path to an enlightenment, who asks
you to simply summarize the path for them, there 's no way to actually
usefully tell them._

I think that has to do more with not being motivated enough to try hard enough
combined with the difficulty of summarizing. It's one thing to try to explain
a paradigm-shifting insight or system to someone who has never paradigm
shifted to learn something. The thing about Haskell outreach, are such
failures even in the face of an audience who has previously undergone such a
paradigm shift.

 _You don 't make the joke to be snarky. You make the joke because you wish
that, this time, it would work, and the learner would skip the path and
achieve the enlightenment_

How is this different from laziness? Smalltalkers similarly gave up trying to
convey how their environment was different, many of them with such snarky
jokes. Then years later, Chris Granger comes along with Light Table, and
transmits what it is quite clearly and succinctly.

~~~
derefr
1\. I perhaps forgot to mention a key thing about the enlightenment process.
Most of the people being _asked_ the question are those further down the path,
but not further-down by much. Journeymen, not masters.

A journeyman is someone who has acquired all the micro-skills they were
missing, and has thus gone on to achieve some level of _intuitive_ grasp of
the enlightenment they sought. They "grok" the skill.

A master is someone who has gone back and thoroughly weeded their mental
garden of the micro-skills they started with and took for granted, and have
begun (though perhaps not finished) replacing those with conscious learnings.
The master desires to attain conscious handles onto each and every part of the
mental schema or process they seek to understand, in order to understand the
enlightenment-requiring mental skill as a _system_ , rather than as a simple
intuition.

A journeyman has a sense that they know the answer—that they "are
enlightened"—and this sense works well enough to guide them when they seek to
_use_ the mental skill they sought to acquire. But, because they do not
understand the enlightenment-requiring mental skill as a system, only as an
intuition, they can give an apprentice on the path no answer that will satisfy
them. This is the "gallows humour" stage of the path.

Yes, masters of the path will be able to guide apprentices. Zen koans are
written by masters, and they are no gallows humour; they tilt the world enough
to allow you to catch sight of the micro-skills the master was attempting to
convey. (In the modern world of analytic philosophy, koans might even have a
hope of being displaced by jargon-laden explanations that can be drudged
through to bash the concept into one's head, like a maths textbook. That
doesn't _sound_ exciting, but it is!)

Light Table is such a koan, created by a master of the path of living-memory-
image reflective systems, seeking to "tilt" as many parts of the system that
Smalltalk is into the light as possible. (Granger's trick was contrasting
those parts to a backdrop of regular, ugly concepts like Javascript and
Electron that aren't part of the enlightenment-inspiring system. The concept-
handles come clear at the visible seams of the Light Table system. Whereas, in
Smalltalk itself, the seams aren't visible, because the system is coherent.
You can't see the muscles of the perfect average face; it's too coherent to
dissect.)

Putting this another way: most people who might be asked about something
aren't teachers with education degrees. They're just students who already
learned something and want to share what they learned. They fail because the
thing is hard to share, and they don't have the _skills about teaching_
required to realize what's making the thing hard to share and to fix it.
(Those that _do_ have such skills, but don't have the time to apply them to
the concept—dissecting it and re-building it in a teachable-to-others
form—usually just keep silent, rather than attempting to share their
learning.)

> Haskell['s journeymen] are such failures [to teach the required micro-
> skills] even in the face of an audience who has previously undergone such a
> paradigm shift.

Ah, but have they? Many people have a natural mind for software engineering.
They take for granted the concepts inherent in procedural code execution on a
CPU or virtual machine; in parsing and lexing; even in pseudo-paradigms like
OOP.

Indeed, it is the rare software engineer who actually experienced any paradigm
shifts regarding Computer Science (for those of us that took a degree in it.)
Usually it's "easy"—meaning natural—for those of an analytical mindset.

All, perhaps, except for that one class you have to take at the beginning of a
CS curriculum, Discrete Maths. A lot of the SwEng "naturals" struggle at that.
Because Discrete Maths is not the same thing as CS. Discrete Maths is, in
fact, maths.

Mathematicians are used to paradigm shifts/"englightenments" (i.e. learning
systematic skills requiring working knowledge of many micro-skills). Each
subfield of mathematics is essentially named for the systematic skill required
to comprehend work done under its aegis. Mathematicians who read work in
various sub-fields, are used to quickly achieving a journeyman's competence in
the relevant systematic skills. Mathematicians who specialize, who enter a
sub-field, must necessarily become masters, if they have any hope of _building
upon_ that work. They must understand the required skill fully. (They must, by
cute analogy, be able to build their Light-sabers from scratch.)

Most software engineers—including Haskellers—are not mathematicians. They
have, other than that one time in Discrete Maths, never experienced the
feeling of climbing toward an enlightenment. Even then, Discrete Maths is
often the lowest grade for a lot of new CS students. They don't yet have this
meta-skill of climbing toward enlightenments—of throwing themselves hard at
formalized jargon using textbooks and references in an attempt to build a new
systematic schema in their brain in just a few days. And they are almost never
told that this is the true skill that their Discrete Maths course is there to
impart into them. It's not about learning graph theory or whatever; that, just
as much as a class on Operating Systems or VLSI or whatever else, is a
practical, concrete skill for a software engineer. The Discrete Maths class in
a CS program is about learning _how to quickly learn_ those kinds of concepts.
It's about discovering that these mental gulfs exist, and learning the skills
required to cross one.

If only it was taught as such ;)

The reason Haskell is uniquely bad, here, is that Haskell was—perhaps
problematically—constructed by mathematicians, people who had already crossed
one such gulf. Haskell's design and ecosystem "reflects" enlightenment on
category theory, somewhat like Smalltalk "reflects" enlightenment on living-
memory-image OOP. Neither system _teaches_ those skills, though. You don't
need to cross the gulf of category-theory to intuit Haskell as a journeyman;
you only do if you want to have the appreciation for the "coherent shape" of
Haskell required to make coherence-preserving modifications to its feature-
set.

\---

The long and short of the way to communicate all the Haskell "stuff"
efficiently, not just monads, is to:

1\. give the learner the meta-skill of Being A Student Of Mathematics (i.e.
becoming a journeyman in new systematic skills by poring over textbooks and
doing problems);

2\. throw a Category Theory textbook at the learner, who is now equipped to
digest it.

Anything less is laziness—though not necessarily on the part of the teacher ;)

~~~
stcredzero
_Light Table is such a koan, created by a master of the path of living-memory-
image reflective systems_

Two answers. 1) So then someone should create a Haskell Koan which has just as
much popular appeal and broad intuitive popular understanding. But then again,
not really, because: 2) Really, get off of this Koan nonsense. It was really
just a very well thought out and effective demo which chose exactly the right
way to get across the benefits of such a system.

 _Whereas, in Smalltalk itself, the seams aren 't visible, because the system
is coherent._

Oh, there are seams! And warts!

 _Indeed, it is the rare software engineer who actually experienced any
paradigm shifts regarding Computer Science_

Really? It sounds like you haven't experienced too many of those kinds of
paradigm shifts. All you know is the math-y kind.

 _Haskell 's design and ecosystem "reflects" enlightenment on category theory,
somewhat like Smalltalk "reflects" enlightenment on living-memory-image OOP.
Neither system teaches those skills, though._

Absolutely wrong. If you delve into the Smalltalk image and class library, it
does teach you OOP!

 _The long and short of the way to communicate all the Haskell "stuff"
efficiently, not just monads, is to:_

 _1\. give the learner the meta-skill..._

 _2\. throw a Category Theory textbook at the learner..._

Either put up or shut up. Anyone can have an esoteric uber language off in a
corner somewhere and tell themselves the world misunderstands them while
circle jerking with like minded folks. Been there, done that. The whole world
is chok full of little groups like that. Stopping at just that is not that
which makes a difference in the world.

What you are saying seems to amount to: "We're awesome! We're the superior
learners, which is why we're superior, but alas poor us, it also makes us suck
at teaching." I call BS. It doesn't matter if your tools are superior, if
their qualities and the community's qualities make them inherently inferior at
gaining mindshare. Either they're so superior, everyone will take the time to
adopt them, or they're not so superior that it really matters that much.

Try and produce stuff. Try and reach people. The world will see who succeeds.
Simple as that.

~~~
derefr
> It sounds like you haven't experienced too many of those kinds of paradigm
> shifts. All you know is the math-y kind.

You know that HN readers are about the 99th percentile of "willingness to
learn random new CS concepts", right? Most programmers know one language. In
fact, most programmers have only ever worked for one company, on one product,
for their whole productive careers so far. Your idea of an "average"
programmer is, in fact, a rare programmer.

> If you delve into the Smalltalk image and class library, it does teach you
> OOP!

That is the exact equivalent of reading a textbook on the subject, except it's
not presented in prerequisite order, so it's slightly harder to digest.

A system that teaches is a system that allows you to notice the micro-skills
you're missing _faster_ than a textbook. A textbook is the brute-force
approach.

Smalltalk is not a system that teaches. It's just a system. You can get out
what you put into delving through it, but you can do that just as well with
any random system. To be pedagogical, a system has to _accelerate_ that
process.

> We're awesome! We're the superior learners, which is why we're superior, but
> alas poor us, it also makes us suck at teaching.

Er, no: people that "know Haskell" (category theory) generally suck at
teaching Haskell (category theory) because people suck at teaching by default,
because they haven't learned the meta-skill of teaching. And even those that
do, haven't yet put in the effort to factor their mental-model of a given
skill to turn it into a _teachable_ skill.

The people that _can_ teach you something about Haskell (category theory),
are, y'know, _teachers_ , that have learned Haskell (category theory) and then
applied educational principles to their understanding of it in order to be
able to teach it well.

Has nothing to do with Haskell, other than Haskell actually having a
relatively-difficult skill in it for people coming from a SwEng background to
learn. Other languages are easier or harder for such people to learn, because
the skills they require are more or less natural given a SwEng background;
and, comparatively, people with a CS or Physics or Electrical Engineering
background have other backgrounds that make different languages have a
different skill-gulf. Haskell (category theory) is easy for mathematicians.
Assembly is easy for electrical engineers. Prolog is easy for DB systems
programmers. Etc. Nothing special about any of them—they're just different
points in knowledge-space, that different people start out closer or further
from because of their backgrounds.

> Try and produce stuff.

I do! Not in Haskell, though. Despite writing the above, I have literally
never used Haskell once in my life. I'm just talking about it as a specific
application of the general principle of skill-gulfs.

> Try and reach people.

Why?

In all of the above, nobody ever said _why_ anyone is trying to teach anyone
else monads. Honestly, I think people just shouldn't bother. No "amateur
teacher" is attempting to teach anyone else graph theory, or linear algebra,
or the x86 ISA. Professional teachers, at universities, do that, because those
are _skills_ independent of any programming language. People generally
understand that teaching these skills is the job of professional teachers, and
that you have to apply yourself as a student, full-time, to learn them.

Well, category theory is such a skill.

In short: stop trying to teach people monads. Petition more schools to teach
people monads (category theory), outside of the context of any particular
language. Then Haskell is just a language, with no skill gulf.

To say that you should "reach people" with an explanation of monads, is like
expecting RDBMS docs to "reach people" with an explanation of relational
algebra. It's not their job. You're supposed to come in with that skill. Their
job is to provide you a thing that you know you want—a solution for a problem
you know you have—given that the skills you already possess give you the
ability to evaluate that solution.

------
solidsnack9000
Monads are a way to model effects -- to mark a computation as using IO (or
not), as being parallel (or not), as having exceptions (or not).

 _Monads in Haskell are used as a mechanism for scheduling evaluation..._

This is only partially true. Any language that tries to be fully declarative
-- which is to say, that tries to make all effects "part of the value" (and in
consequence, part of the type) -- faces the same problems as Haskell. Idris, a
strict language, uses monads to model effects.

The C language doesn't make effects part of the type signature, but C program
verifiers _do_ add effects modeling. This can be with regards to aliasing
(which writes/reads could affect another pointer?), termination or timing.

It's the underlying interest in program verification that drives the use of
monads in Haskell, in part because it's program verification that drives the
interest in Haskell to begin with.

~~~
egillie
I can’t upvote this enough. I always wish monads has been described to me this
way from the get-go, it would have saved me a lot of time.

------
tathougies
After many years of Haskell, I was playing around in C the other day for fun.
I'm writing low-level network code, and my code looks like this.

int err = socket(...) if ( err < 0 ) { do_something; }

err = listen(...) if ( err < 0 ) { do_something; }

Haskell's bind operator >>= makes abstracting this really simple. I wish I had
it in C.

~~~
xamuel
Could you elaborate? As a C guy, I'm interested what you think would be
simpler than that simple code you posted. Structurally, what you just posted
(minus the details about what "socket" and "listen" mean) could be understood
by a child. I'm skeptical the corresponding Haskell >>= construction would be
so understandable.

~~~
lmm
Children aren't born knowing C, and people who learn functional first don't
tend to find it any harder. (If anything it's easier, since the meaning of "="
in Haskell is much closer to its usual meaning than the meaning in C).

~~~
tathougies
Yeah, I agree. Functional languages are often more intuitive to beginners. My
pet theory is that the reason CS people tend to be somewhat 'odd' is that most
people do not think imperatively, so when imperative programming is propped up
as the gateway, it screens out people who would otherwise flourish in the
field.

~~~
magicalhippo
> most people do not think imperatively

When trying to solve a problem by thinking it through, don't most people think
imperatively? Ie by a sequence of steps (statements) that modify the world
around them (state) to reach the goal?

~~~
tathougies
I’m not a psychologist. I can’t talk about most people. I do not. I break
problems down into sub problems and then think of how to combine results to
achieve the answer. I repeat this until my subproblem is trivial. This lends
well to the compositional approach of functional languages. Imperative
languages require taking the extra step of scheduling. I think many people
think like me but due to the prevalence of imperative languages, do not pursue
cs.

~~~
magicalhippo
Well, scheduling is important. I can't connect my new phone to the computer
until I've taken it out of the box it came in.

It feels like I'm leaning to the imperative side. When breaking down the
problem into substeps, I'm already thinking about the sequencing which will
later dictate the scheduling.

I've been teaching Scratch[1] to 11-13 year olds for a few years now, and most
seem to pick it up pretty quick. However a few struggle (while still being
motivated enough to keep trying). Would be interesting to see how it went with
a functional Scratch version.

[1]: [https://scratch.mit.edu/](https://scratch.mit.edu/)

~~~
lmm
> Well, scheduling is important. I can't connect my new phone to the computer
> until I've taken it out of the box it came in.

That's not scheduling, that's a data dependency which functional style handles
very well. Imperative would be more like deciding you have to unpack the
charger and then unpack the phone and then charge the phone, when in fact it's
fine to unpack them in either order.
[http://www.lihaoyi.com/post/WhatsFunctionalProgrammingAllAbo...](http://www.lihaoyi.com/post/WhatsFunctionalProgrammingAllAbout.html)
has an extended example.

~~~
magicalhippo
Good point, thanks for the correction.

------
thanatos_dem
> The incomprehension of the newbies is matched in intensity only by the
> smugness of the experts.

Oh good. A write up that’s aware of how unapproachable monads seem. Maybe this
will actually be a good, plain-English breakdown.

Four paragraphs later:

> Monads, as a mathematical concept, can be always used as a part of the
> semantic model, but whether they transpire into the type system of the
> object language is a matter of design choice.

Oh... well, never mind.

~~~
rhencke
The best practical description of monads I've heard, with apologies as I do
not remember the quote's author:

What if, in C++, the semicolon was an operator you could overload?

~~~
FPGAhacker
So I haven’t quite grasped monads or their usefulness.

Your “what-if” is completely meaningless to me, and I’m sure it’s my lack.

Could you expound upon the implications?

To me it is like saying: what if you could redefine the period at the end of a
sentence....ok, but why?

~~~
v_lisivka
Monads are easy to understand for imperative programmers.

Imagine, you have code "var a=new A(); a.foo(x); a.bar(y); print(a);". How you
reimplement it when "a" is readonly structure, not even a object? You will
need to implement functions "foo(a, x) -> a" "bar(a, y) -> a", and so on. If
you do so, you will have monad.

~~~
AnimalMuppet
What's not so easy to understand for an imperative programmer is this: Why
should I jump through those hoops just to have a readonly structure? Why not
just, you know, program imperatively?

I think this is a big part of the problem explaining monads. If you don't have
the problem, you don't care about the solution. And if you're an imperative
programmer, this looks like saddling yourself with a problem you don't need to
have, and then jumping through bizarre hoops to deal with the complications
caused by a set of restrictions that you didn't need in the first place.

I think instead the right approach is to sell people on the benefits of
immutability, and talk much less about monads. When they start to need them,
they'll start to see why they want them. Once they see them solve some
problems that they actually have, then they'll start to get it.

~~~
joel_ms
> What's not so easy to understand for an imperative programmer is this: Why
> should I jump through those hoops just to have a readonly structure? Why not
> just, you know, program imperatively?

Beacuse imperative programs are rife with "statements". Opaque lines of code
that may or may not do something, somewhere, which cannot be manipulated as
first class objects.

Why would you ever wanna program with that weird restriction?

~~~
AnimalMuppet
I can't tell if that's parody or not...

------
skybrian
A side point since the author pointed to Agda as being more pure than Haskell:

In many practical situations, computations have deadlines, which may either be
a timeout or the user giving up and cancelling. Languages that guarantee that
functions terminate aren't as useful as you might guess, because they don't
say anything about how long it will take. (It could be millions of years.)

A guarantee that a function will terminate is useful when you can avoid
evaluating the function at all, while relying on a function call's existence
(without compile errors) as a proof that the result exists.

It's not too often that you want to know that an answer exists without knowing
the answer. This mostly comes up in mathematics when proving things.

~~~
lmm
> Languages that guarantee that functions terminate aren't as useful as you
> might guess, because they don't say anything about how long it will take.
> (It could be millions of years.)

That's a theoretical problem, but is it one that comes up in practice? In my
experience those languages eliminate the overwhelming majority of long-running
functions, so are about as useful as you might think.

~~~
skybrian
I don't think it comes up much in practice because bugs due to non-terminating
code don't actually happen much to begin with (compared to all bugs that cause
hangs or timeouts), and when they do they are often found via testing. We know
that a function terminates for any inputs it was tested with.

But I have little experience with these languages (I've mostly read about
them) so I'd be interesting to hear about your experiences.

~~~
joel_ms
>I don't think it comes up much in practice because bugs due to non-
terminating code don't actually happen much to begin with (compared to all
bugs that cause hangs or timeouts), and when they do they are often found via
testing. We know that a function terminates for any inputs it was tested with.

None of these are acceptable in Agda actually, since they would all be partial
functions (i.e. functions that are not defined for some inputs.) The simplest,
intuitive explanation is that since Agda has dependent types it needs to be
able to evaluate functions during typechecking, and if you allow partial
functions (e.g. infinite loops, halting functions, functions that timeout, or
functions that are undefined for untested input) the typechecking would be
undecidable.

Instead Agda has a bunch of built-in tools to help the compiler figure out if
a function is total (the opposite of a partial function, meaning it's defined
for all possible inputs). Things like the termination checker can detect a
subset of all terminating functions, and sized types let the programmer aid
the compiler in figuring this out. There are also language pragmas to overide
this totality check on a per function basis.

I've implemented an algorithm, that I knew on paper terminated, and had it
rejected by Agda, although I'm not experienced enough to know how easily these
things are solved in general.

~~~
skybrian
Yes, static analysis is very useful for understanding what the source code
will do for inputs you didn't test. Testing is rarely exhaustive. However,
testing will find performance issues where static analysis won't.

To quibble with "none of these are acceptable in Agda": it seems unlikely that
Agda or anything like it would detect hangs or timeouts due to code not
meeting its deadline. Deadlines and performance constraints are not part of
any type system I've ever heard of. Furthermore, performance is dependent on
the compile toolchain, the runtime, and what else is running on the same
machine, so it's not even well-defined for static analysis unless you make a
bunch of deployment-specific assumptions.

A language may guarantee that a function is total in a theoretical sense, but
you might not be able to calculate its value for some inputs in practice.
(Consider something like "a ↑↑↑ b".) So this guarantee is subtly different
from what most users of computer systems would actually want.

Of course, as a another commenter pointed out, this sort of practical bug-
finding is not actually what the termination guarantee is for.

~~~
lmm
> However, testing will find performance issues where static analysis won't.

Not my experience, at least if we're talking about constructive type systems
(I don't find best-effort-style "static analysis" useful, personally). I don't
think I've ever seen any "normal" software testing regime (junit etc.) catch a
performance issue that wasn't a straightforward infinite loop of the kind that
would be obvious in something like Idris.

> quibble with "none of these are acceptable in Agda": it seems unlikely that
> Agda or anything like it would detect hangs or timeouts due to code not
> meeting its deadline. Deadlines and performance constraints are not part of
> any type system I've ever heard of.

Languages with a more detailed type system that can express maximum evaluation
time (in terms of e.g. some set of "primitive ops" for that language) do
exist, though I wouldn't consider any of them remotely practical.

> A language may guarantee that a function is total in a theoretical sense,
> but you might not be able to calculate its value for some inputs in
> practice. (Consider something like "a ↑↑↑ b".) So this guarantee is subtly
> different from what most users of computer systems would actually want.

Sure. Requiring totality doesn't eliminate all possible code-runs-for-too-long
bugs, only the vast majority of them. I don't see that as a reason not to do
it.

~~~
skybrian
To clarify:

By "static analysis" I mean anything you can learn from studying source code
without running it. (Though, with Agda this isn't a clear distinction; maybe
we could talk about what we can extrapolate about whatever the compiler didn't
evaluate.)

I was speaking of testing broadly to include manual QA and running benchmarks.
A continuous benchmark will give a graph of the system's performance for
certain actions. It's typically not pass/fail, but if the developers notice a
regression they can investigate. These slowdowns just aren't correctness bugs
as commonly understood and many places don't notice performance issues until
after the code goes live and it's noticed via monitoring. (Benchmarks are
tricky to write, tricky to run consistently, don't generalize, and miss a lot
of things, but nonetheless measure things that static analysis doesn't even
try to do.)

"Vast majority": I guess this depends on what you put in the denominator? I
can certainly believe that Agda programmers don't see performance issues. But
I'll confess ignorance: is this used for anything other than doing math?

I'm thinking more along the lines of: if a more mainstream language had
totality checking, what bugs would this actually find in the code that runs in
data centers? Plenty, I'm sure. But I can think of lots of things it wouldn't.
It's one method of bug-finding among many.

~~~
lmm
> I was speaking of testing broadly to include manual QA and running
> benchmarks. A continuous benchmark will give a graph of the system's
> performance for certain actions. It's typically not pass/fail, but if the
> developers notice a regression they can investigate. These slowdowns just
> aren't correctness bugs as commonly understood and many places don't notice
> performance issues until after the code goes live and it's noticed via
> monitoring. (Benchmarks are tricky to write, tricky to run consistently,
> don't generalize, and miss a lot of things, but nonetheless measure things
> that static analysis doesn't even try to do.)

Well we've got to compare similar levels of effort. I think most "normal"
teams would find it easier to adopt a more detailed type system than to set up
a continuous benchmark and use it effectively. I could be wrong.

> "Vast majority": I guess this depends on what you put in the denominator? I
> can certainly believe that Agda programmers don't see performance issues.
> But I'll confess ignorance: is this used for anything other than doing math?

> I'm thinking more along the lines of: if a more mainstream language had
> totality checking, what bugs would this actually find in the code that runs
> in data centers? Plenty, I'm sure. But I can think of lots of things it
> wouldn't. It's one method of bug-finding among many.

I don't know so much about Agda specifically. I agree with thinking about
mainstream code that runs in data centers. My thought process was: of the
code-runs-too-long bugs I've seen in my career, how many of those could have
happened if I'd been working in Idris with totality checking on? I can only
think of one (which was due to regex catastrophic backtracking), and I can
think of several that would have been caught (the case where the test used the
wrong overload of the function being the most production-impactey one).

My experience is that while other methods of finding bugs do exist, the
cost/benefit for type systems is just so much better than anything else that I
wouldn't even think about using any other technique until I'd tried type
systems first.

------
dustingetz
If you think Function Composition is a good thing, then it is very natural to
want composition for functions of more interesting types than T→U, for example
T→Promise[U] which chains intuitively but not by type.

How did I do?

~~~
jimbokun
I don't know, but I think "Chain" might be a better name than "Monoid" to
introduce the concept and capture the main idea.

~~~
dan-robertson
Well “Monoid” is a different sort of composible than something like like T ->
Promise U.

I do agree that Monoid and Monad aren’t really great names for anyone who
doesn’t come from an algebra background (ie basically anyone).

------
galaxyLogic
The question monads answer is: How do I make the components of my program
recombinable in ways that the result is also a monad.

Monads are like LEGOs, you can combine them in any way and what you get is
something to which you can attach still more lego-pieces. It doesn't matter
how exactly lego pieces are able to connect together. What matters is that you
can combine any Legos together with that same mechanism and get as result
something to which you can attach even more Legos. Maybe a better name for
Monad would have been "Lego".

------
dan-robertson
I think one thing one should ask is:

If monads are the solution to X, and we add a different solution to X to the
language, then what are monads for?

Let’s say you have imperative IO. Then promises are a Monad but then you could
just add language support for async and promises are not really needed as a
Monad anymore.

A more functional replacement for monads could be something like algebraic
effects. These could replace monads completely or they could be like multi
core ocaml and only give you one shot continuations (so not allowing for list
or other multi-shot monads).

In this context I like to think about the continuation Monad. In Haskell:

    
    
        newtype Cont r a = Cont { runCont :: (a -> r) -> r }
    

This means “some computation yielding a _r_ which got paused part-done having
just yielded an _a_. In some sense all monads are expressible in terms of
Cont. I feel like this is largely the essence of why it is useful to have
monads: they express computations which happen with some context, able to
switch back and forth between things in the context and things in the normal
computation. I don’t think sequencing evaluation is so important to the
definition.

------
bcheung
I like to think of monads as a supporting sidecar wrapper. It's almost like a
plugin system; you can extend some base computation to have additional
functionality for free.

This becomes really obvious how much boilerplate can be "lift"ed in something
like the Flare library for PureScript ([https://david-
peter.de/articles/flare/](https://david-peter.de/articles/flare/)).

Ex:

lift2 ( * ) (int "a" 6) (int "b" 7)

This generates 2 text fields, hooks up the change events, plumbs them into a
third UI component that renders the product of the 2 fields.

Fundamentally we are just saying we want "a * b" but the monad / sidecar does
all the extra work to make UI components and hook up the change events.

------
tikhonj
This is a somewhat misleading way to think about Haskell and monads—it puts
the cart in front of the horse and obscures both how Haskell deals with
effects _and_ how monads are more general and useful than just IO. This blog
post ends up mystifying more rather than demystifying.

Then again, this shouldn't be a surprise from an article that describes the
Haskell ideas as "the Haskell monadic dogma"! You wouldn't expect a
particularly charitable or educational account to start like that.

A much better way to think about it is to think about IO without considering
monads at all. One of Haskell's core characteristics is that it explicitly
separates code with side effects from code without side effects using the IO
type. This is a powerful design _in and of itself_ —it's a tool for helping
programmers manage effects and not a hack around laziness.

Personally, more tools to manage effects is _exactly_ what I want. After all,
even in languages, code I write is carefully organized to separate most side-
effects (API calls, database accesses... etc) from the domain logic, but this
happens solely through convention and code organization. Having tools _in the
language_ to express the same separation is powerful.

Note how I never needed to talk about monads here—IO is the important concept.
With this in mind the real question is not "why does Haskell need monads?" but
"why are monads a _useful_ abstraction?". This gets a bit long for an HN
comment, but I wrote a blog post with my thoughts a few years back. It started
like this:

> _I believe the notion that Haskell uses “monads” to enforce purity is rather
> misleading. It has certainly caused quite a bit of confusion! It’s very much
> like saying we use “rings to do arithmetic”. We don’t! We use numbers, which
> just happen to form a ring with arithmetic operations. The important idea is
> the number, not the ring. You can—and most people do—do arithmetic without
> understanding or even knowing about rings. Moreover, plenty of rings don’t
> have anything to do with arithmetic._

> _Similarly, in Haskell, we use types to deal with effects. In particular, we
> use IO and State and ST, all of which happen to form monads. But it is still
> better to say we use “the IO type” to do IO or that we use State for state
> than to say we use “monads” to do IO or state._

You can read the rest here: [http://jelv.is/blog/Haskell-Monads-and-
Purity/](http://jelv.is/blog/Haskell-Monads-and-Purity/)

This is by no means a _definitive_ discussion of monads or anything like that;
it's just a perspective I found useful for understand what Haskell gets by
having a monad abstraction and why it's interesting.

------
mattdeboard
> Some of the words above, especially the meandering second sentence, seem to
> be written to baffle rather than inform.

Ironic. The message he's referring to is the only explanation for what a monad
is that has made any sense to me.

~~~
danidiaz
> The essence of monad is thus separation of composition timeline from the
> composed computation's execution timeline

I also feel that it makes sense. Haskell prefers expressions to statements.
When needed, statement-like things are constructed from expressions.

------
chadcmulligan
I'd say the problem with all these Monad explanations is the people reading
them are in general object oriented programmers (like me) and the problem that
exists in functional programming just isn't an issue in C++ / Swift etc. Its
never explicitly stated because functional programmers are just so used to it,
its like air.

So in a very naive way - here's the problem, When you apply a function and get
a result you have no state - the function doesn't remember what happened. In
object oriented programming you always have the state - its in the object, or
other objects floating around, and methods can always reach out into
surrounding objects to get bits of state it needs, pure functional programs
can't do this - they have no state except the things that are passed as
parameters. The haskellian way around this is to pass around the context of
what just happened - vaguely the context is the surrounding bits of data you
need to keep track of.

Monads, functors and so on are the ways that haskell keeps and manipulates the
context and the value. The rules also have a very nice mathematical theory
which they fit into. Most haskell intros start from maths and shoe horn the
code into this - its not really necessary. This clarified it for me
[http://blog.sigfpe.com/2006/08/you-could-have-invented-
monad...](http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-
and.html)

------
k__
It's a way to wrap values in a mathematically defined way.

You can do it differently if you like, but you have to define the semantics
yourself and find out about edge cases that math has already solved, which is
the problem that is searched here, I guess...

------
platz
Unfortunately, the Haskell wiki is not a great place to point beginners. The
wiki's growth was rather... organic.

------
benadamx
does haskell support bofa monads?

------
tufbhuf
[http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html)

~~~
emmelaich
Very nice!

