
Monads are a Class of Hard Drugs (2008) - paul-woolcock
http://lambda-diode.com/programming/monads-are-a-class-of-hard-drugs
======
tikhonj
This article is flawed at its core: monads do not, in general, compromise type
inference and they are _not_ all about sequencing state.

Sure, they are used for state and IO, but they can do far more, like
nondeterministic programming, continuations or even nothing at all.
Ultimately, I would say monads are about composition: they let you define
exactly how to compose computations.

Additionally, monads do not break type inference. Having a type for every top-
label binding is considered good style in Haskell, but it is still optional. I
could see this practice being confused for type inference not working. There
are of course language extensions that do break type inference in certain
cases, and sometimes it is impossible even with the standard language, but it
works almost all of the time even with monads.

Also, and this is probably just because this article is for years old, it
really overstates hire bad Haskell performance is. In my experience, even
naively written code runs relatively fast--certainly fast enough for me--in
most cases. I believe this has really improved in recent times. Haskell is
certainly better in this regard than most other high-level languages.

~~~
beering
It's probably true that simply written Haskell code is fast enough for most
uses. The trouble comes when that code isn't fast enough, or should be faster.

How do you make Haskell code faster? Apparently it's somewhat like writing
fast Java code: avoid most features, use primitive data types, and write like
you're writing C. Haskell code optimized for performance ends up like messy C
code more often than not, except that unsafePerformIO isn't such a prickly
topic in C.

Even Haskell professionals often don't know when "free" GHC optimizations will
kick in, because the optimizations can be so picky, even after you remember to
apply some forgotten language pragma. Writing performant Haskell is a black
art.

~~~
fusiongyro
Only one of the Haskell shootout programs uses `unsafePerformIO` now (n-body).
While it looks to me like most of them make fairly heavy use of Foreign and
thus low-level types, I don't think this automatically makes Haskell code like
messy C code. After all, there still isn't pointer arithmetic, types don't
magically transform into other types behind the scenes, and there's still a
fair degree of non-strictness. More importantly (and contrary to C's
"messiness") it's seldom difficult to take Haskell optimized with these low-
level features and wrap it up in a nice high-level package for use from
elsewhere. It's a curious and wonderful property of GHC that you can enable
all the extensions you want on a module-by-module basis without creating a lot
of cross-module drama. These are properties other languages should strive for.

Writing exceptional, highly performant Haskell is still a black art, sure. But
writing performant Haskell is basically writing Haskell with a modicum of
knowledge and respect (less lazy IO, more ByteString, more iteratees, etc).
Writing highly performant Haskell is becoming easier and easier; the runtime
system will give you detailed statistics about where your problems may lie,
tools like ThreadScope can give you all the information you'd ever want, and
many of the commonly-used libraries are heavily optimized for speed (Text,
ByteString, iteratees, etc) which you benefit from basically for free.

It's not a panacea but I haven't ever had to use anything more sophisticated
than strictness annotations, and even that I use pretty sparingly. Like the
best black arts, it's very seldom necessary, and becoming more seldom every
day.

As a side note, I would love to see an alternative "honest" shootout where
programs must not do much overt "doping" (such as using Foreign and
`unsafePerformIO`) because it would be interesting to compare the "native"
speeds of different languages, with the kind of code the best normal people
would write for themselves or their employer. Haskell has always abused its
low-level facilities for higher placement in the shootout than it really
deserves.

------
Strilanc
Two glaring errors in here:

> The essence of monads is to use abstract types to enclose a mutable state
> [...]

This is a common misconception, but terribly misleading. The "essence" of
monads are the bind and return (or equivalent) functions, allowing decoupled
transformations to be performed. The fact that you can write bind and return
functions for a type that encapsulates the idea of a side effect is secondary.

Would you say "the essence of iterators is to encapsulate infinite sequences"?
Infinite sequences happen to be iterable, but to say that they _are the
essence of_ iterators is downright wrong. The essence of iterators is
traveling through a collection item by item, as defined by the
MoveNext/Current (or equivalent) functions.

> For one thing, when you use a monad, you get hooked to it: the type
> constructors of the monad start to appear in the signatures of your
> function.

If you have functions that should have nothing to do with a monad ending up
coupled to it, then you're probably doing it wrong. Monadic methods let you
"lift" functions, so they don't have to be coupled:

    
    
        // bad: unnecessarily coupling your function to a monad
        IEnumerable<int> Increment(IEnumerable<int> sequence) {
            var r = new List<int>();
            foreach (var e in sequence) r.Add(e + 1);
            return r;
        }
        ...
        var incrementedList = Increment(list);
    
        // good: using monad method to lift a non-coupled function
        int Increment(int b) { return b + 1; }
        var incrementedList = list.Map(Increment);

~~~
magnusjonsson
For the first point, I think the author is aware that you can do more than
just mutable state using monads:

> But mutable state covers everything that is not pure computation and that
> includes debug statements, input and output, system calls, network access,
> getting the time of the day or even throwing an exception. So the monad you
> are using has to provide all these services. Of course not every monad has
> everything you need so you have the IO monad, the Maybe monad, the STM
> monad, and so on. Whenever you have a C binding, you get a monad.

The second point example doesn't seem to address the author's concerns. The
author is complaining about what happens when you try to compose N components
that all disagree on the model of computation. OCaml has a more practical
default model of computation so less such plumbing is needed.

------
csense
Anyone who understands monads is on a class of hard drugs.

I'm still looking for someone who can present a cogent explanation of monads
using only Python.

(I'm willing to replace "Python" in the above challenge with "any language I
know well," which would include Python, C, C++, Java, JavaScript, and quite a
few others which I won't bother to list here. Notably lacking from this list
are Lisp, Scheme, Caml, Haskell, Prolog, Erlang, and most other languages
whose view of the world is "weird." NB not that I'm against learning such
languages; it's more a matter of available tutorials and time.)

~~~
jayferd
What I wish someone had explained to me earlier was that Monads are an
abstraction level up from most things you ever program with. Learn specific
ones, and the general picture will start to make more sense. Avoid the IO
monad until you understand, in order:

\+ the List monad (probably the easiest for python people, since it's very
similar to list comprehensions)

\+ the Maybe monad (kind of a degenerate case)

\+ the State monad

And it's much easier if you do it in Haskell, trust me. "Learn You A Haskell
For Great Good" is a nice easy intro to the basics.

~~~
codewright
>\+ the Maybe monad (kind of a degenerate case)

Hey. Hey. heyyyyy.

I've used bind + maybe monad 'ish stuff in Python to clean up code previously
littered with redundant if checks.

~~~
tikhonj
He probably means its a degenerate case of the list monad. Which is a useful
way to look at it: a Maybe value is like a list of length at most 1.

You can also look at it as a degenerate case of Either. (That is, Maybe a ~
Either () a.)

This is "degenerate" in the mathematical sense, much the same way you can
think of a point as a degenerate circle.

------
tome
_For one thing, when you use a monad, you get hooked to it: the type
constructors of the monad start to appear in the signatures of your function._

Yup, that's the point![1]

[1] Or rather, one of _many_ points.

~~~
tel
I used to feel this way too, so I'm not trying to be coy, but really that's a
property of a (some) Functor(s), not a Monad (except in that all Monads are
also Functors).

Monad is just the pattern where you combine Functors. Some Functors can be
pattern matched upon to "escape" their values. Functors like IO cannot. IO
would still be (academically) useful if not for its Monad instance... you'd
just only be allowed a single effect in the entire program!

