
Monads aren't as hard as you think - yingw787
https://bytes.yingw787.com/posts/2019/12/06/monads/
======
hintymad
I find these introductory articles ultimately misleading. Monad is never hard,
just like group is never hard, vector space is never hard, lattice is never
hard, manifold is never hard, and on and on. What's hard is to understand not
just one single concept but layers of webs of abstract concepts, along with
all the theorems and ingenious ways of applying them. What turns people away
is not the difficulty of monad or functional programming in general, but lack
of incentives. Why don't we just be upfront and tell people: XXX is hard, but
you know what? If you master it, here is what you will get out of it.

~~~
yingw787
@hintymad thanks for sharing! For me, monads seemed hugely difficult to learn,
and so I never got started with them at all. Had I just known that they were
not as hard as I thought they were (which I think is different from not hard)
I might have started earlier. I’m hoping that by sharing some of my
understandings I can help people avoid some of my own hesitation.

What I really like about the HN community is the notion of shipping even if
you’re embarrassed by it, because the future good or bad is built by the
people who show up.

~~~
recursive
It only seems not-hard for people that already understand them. Here's where
the post shows _just how simple_ monads can be.

> A monad is a data type (e.g. int) that encapsulates some control flow (e.g.
> try/catch).

But... I'm already lost. How would an integer "encapsulate" control flow? Like
different integer values would represent different errors that could occur? Or
you could use integers as a representation for a language that has try/catch
as a feature? Even in this simplest case, if you don't already grok monads,
it's somehow not illuminating for me. YMMV

~~~
setr
that's just poor wording -- the first example wasn't supposed to be linked to
the second example;

A better sentence is probably

A monad is a datatype (e.g. Either<T,U>) that encapsulates some control flow
(e.g. if T != null return T else U)

if I understand it correctly

------
rdegges
This is one of my favorite songs about Monads:
[https://www.youtube.com/watch?v=BoJGIqyriCc&list=PLw0jj21rhf...](https://www.youtube.com/watch?v=BoJGIqyriCc&list=PLw0jj21rhfkM-
etwRXGDIko0Ou-QLdeMq&index=6&t=0s)

~~~
yingw787
@rdegges thanks for sharing!

------
maxdamantus
> A monad is a data type (e.g. int) that encapsulates some control flow (e.g.
> try/catch).

Ignoring the fact that `int` itself doesn't have much to do with monads (it's
presumably just giving an example of a type), I've always disliked the notion
of talking about monads being types or vice versa.

The way I think of it is that the monad itself consists of the monadic
operations for some set of values. The "IO monad" consists of the `bind` and
`unit` functions to do with values of types `IO<T>` for any `T` (eg,
`IO<Int>`, `IO<String>`, `IO<IO<Int>>`). Similarly, the "Maybe monad" consists
of the `bind` and `unit` functions to do with values of types `Maybe<T>` for
any `T`.

Both of these monads can be said to implement a common interface, which might
be expressed using the following Java-like code (using Java just to emphasise
that there isn't really[0] anything magical here):

    
    
      interface Monad<M> {
        <T> M<T> unit(T v);
        <T, U> M<U> bind(M<T> a, Function<T, M<U>> f);
      }
    

If we were to define an "IO monad", we would expect it to implement
`Monad<IO>`, therefore an "IO monad" is really just a value of type
`Monad<IO>`:

    
    
      Monad<IO> ioMonad = new Monad<IO>() {
        <T> IO<T> unit(T v) { .. }
        <T, U> IO<U> bind(IO<T> a, Function<T, IO<U>> f) { .. }
      };
    

This is actually pretty much exactly how it works in Idris for example, where
`Monad IO` is itself a type, and you can actually ask for the `Monad IO`
implementation by writing, funnily enough, `the (Monad IO) %implementation`:

    
    
      Idris> 4 + 5 -- to demonstrate the REPL
      9 : Integer
      Idris> Monad IO
      Monad (IO' (MkFFI C_Types String String)) : Type
      Idris> the (Monad IO) %implementation
      constructor of Prelude.Monad.Monad (\meth, meth, meth, meth => io_bind meth meth) (\meth, meth => io_bind meth id) : Monad IO
    

[0] The only reason it doesn't actually work is because Java doesn't support
higher-kinded polymorphism, so you can't pass type-level functions such as
`IO` or `List` as type parameters, therefore you can't actually write `M<T>`
as appears in the interface body.

------
tutfbhuf
> The only way to really understand a complex concept is to see it from as
> many different angles as possible.

\- Bartosz Milewski

------
z3t4
So a simple div function (x/y) turned into several classes and boilerplate.
For what again? So that the program would not crash if it got into a bad
state? JavaScript please give me back early errors! I hate that Promises just
swallow errors. If there is an unexpected state, something that should _never_
happen,then GTFO immediately, print the stack, give me a friendly error with a
line nr to make it simple to debug, create a memory dump. Don't continue the
execution with a poisoned state! If you want a _safe_ program, it should exit
at even the slightest smell of error. Not make my heart beat 300 bpm or put
the airplane into a nose dive, or show the wrong altitude so that the plane
flies into a mountain. So you are feeling anxious that your program can throw
an error at any time? Don't be, that's much better then the alternative
described with the airplane above. You solve the edge cases, and gracefully
_handle_ errors that can be worked around, but for unexpected errors, like the
altitude showing -2 million. (int overflow or something) you want it to crash
(the program, not the airplane).

~~~
WorldMaker
Promises don't swallow errors, they pass it up to whatever next promise gets
chained or any .catch() handlers registered. Browsers have an event to listen
to for unhandled promise rejections at the global level for all those places
you fired off a promise but forgot to do anything with it. (All the major
browsers now show error messages and stack traces in the dev console as a
default action for all unhandled promise rejections. They don't stop page
execution as browsers decided against that back in the early IE/Netscape era
to be more graceful in a complex event-based world. Node also spits out
unhandled promise rejections to the console and hard quits a process now.)

But the juicy benefit to Promises is the (monad-based) transformation to
async/await notation. Async/await notation gives you back the try { } catch {
} synchronous-looking error catching that you are wishing for, all it takes is
a couple new magic keywords and your code looks very similar to what it would
in a magic synchronous world without promises. (Most current browsers and
current versions of Node even directly support async/await today [0] directly
out of the box, no need for a compiler/transpiler tool to do the
transformation for you.)

[0] [https://caniuse.com/#feat=async-
functions](https://caniuse.com/#feat=async-functions)

~~~
z3t4
In JavaScript there are two types of errors: First class errors that you can
return, pass around and bubble up. And then there are exceptions that means
the program did something unexpecting. Promises make no exception and does not
treat them as first class citizens.

~~~
WorldMaker
Promises make _future_ exceptions. If you aren't paying attention "in the
future" to catch those exceptions, they bubble to an error handler
(window.onUnhandledRejection [0]), so it acts as _both_ types of JS errors,
depending on how you want to pay attention to it.

async/await syntax lets you catch promise rejection exactly like you would any
other type of JS exception with a try {} catch {}.

[0] [https://developer.mozilla.org/en-
US/docs/Web/API/Window/unha...](https://developer.mozilla.org/en-
US/docs/Web/API/Window/unhandledrejection_event)

~~~
WorldMaker
To bring things back on topic: async/await syntax in JS inherits its magic
almost directly from Haskell's do-notation and is exactly an example of why
exploring Monads has been practically useful for languages far outside of the
"academic ivory tower walls" of Haskell.

------
ggm
The only monad anyone ever talks about in my hearing is the IO monad. I
sometimes imagine all monads reduce to the IO monad and some specified side
effect by intent.

Except of course the whole point of monadic thinking appears to be "no side
effects" so .. I'm stuck

~~~
edflsafoiewq
If you know any linear algebra here's an example that has nothing to do with
side effects:

Let X be any set. Let mX be the set of formal linear combinations of elements
of X (that is, expressions like 3x₁+2x₂ where x₁ and x₂ are elements of X).
Then m is a monad.

fmap is variable substitution. Ex. fmap [x₁->y₁, x₂->y₂] (3x₁ + 2x₂) = (3y₁ +
2y₂).

join is simplification of nested expressions. Ex. join (3(2x₁) + 2(x₁ + 2x₂))
= (8x₁ + 4x₂).

>>= is substitution and simplification. Ex. (3x₁ + 2x₂) >>= [x₁->3y₂, x₁->-y₂]
= (3(3y₂) + 2(-y₂)) = 7y₂.

A Kleisli arrow X->mY is a system of linear equations, ie. a matrix

    
    
        x₁ = 3y₁ + 6y₂
        x₂ =  y₁ - 2y₂
    

>=> is composition of linear systems, ie. matrix multiplication.

Whereas in IO you think of f >=> g as meaning "first f, then g", here f >=> g
is the matrix product fg, ie. "first g, then f".

~~~
perl4ever
I was like if m in mX is a monad, how is m not a function? Or if it is a
function, how is it different from functions in general?

Googling, I found the following helpful comment:

"...while addition and multiplication are both monoids over the positive
natural numbers, a monad is a monoid object in a category of endofunctors:
return is the unit, and join is the binary operation. It couldn't be more
simple. If that confuses you, it might be helpful to see a Monad as a lax
functor from a terminal bicategory"

~~~
perl4ever
Wait, I think I may have found a good explanation:

"A few months ago Brent Yorgey complained about a certain class of tutorials
which present monads by explaining how monads are like burritos.

At first I thought the choice of burritos was only a facetious reference to
the peculiar and sometimes strained analogies these tutorials make. But then I
realized that monads _are_ like burritos."

[https://blog.plover.com/prog/burritos.html](https://blog.plover.com/prog/burritos.html)

