
Functor, Applicative, and Monad - TheAsprngHacker
https://typeslogicscats.gitlab.io/posts/functor-applicative-monad.html
======
wh0knows
I don't think this article is very useful. It doesn't adequately provide an
introduction to OCaml code (or adequately explain what a given code snipped is
doing) and yet frequently defers to just code to explain a concept. It's an
unrealistic expectation to expect an unfamiliar reader to simultaneously infer
what a particular code snippet is doing then also go a level deeper and
understand the concept that is trying to be presented.

This article is most readable to those who already understand OCaml code, and
if you can already read OCaml code you already understand these concepts.

~~~
TurboHaskal
You claim that OCaml programmers are already versed in category theory but
that’s not correct. The first exposure usually comes with concurrency
libraries such as Lwt which isn’t very old nor in the standard library, and it
provides syntax sugar so you can start being productive right away.

You can write perfectly fine production OCaml programs without knowing what a
monad is. Some may, but a lot of OCaml devs don’t care about them.

~~~
hope-striker
This is correct, and in contrast to Haskell, where monads are a core part of
the language (they are used in the definition of do-notation and list
comprehensions) and currently the mainstream way to do IO.

~~~
notfashion
This is misleading. There's no place in the specification of Haskell that
specifically defines monads as part of the language. They aren't a language
feature. They are a pattern which happens to be expressible in Haskell, and
which is supported by libraries:

"Haskell's built in support for monads is split among the standard prelude,
which exports the most common monad functions, and the Monad module, which
contains less-commonly used monad functions. The individual monad types are
each in their own libraries and are the subject of Part II of this tutorial."

[https://wiki.haskell.org/All_About_Monads#Monad_support_in_H...](https://wiki.haskell.org/All_About_Monads#Monad_support_in_Haskell)

The fact that monads are used to implement parts of the language doesn't make
them a core part of it. Techniques used in implementation aren't the same as
language features.

~~~
sullyj3
Is do notation a language feature?

~~~
verttii
it's syntactic sugar

------
_hardwaregeek
An important realization that I had was that monads/functors/applicatives
aren't patterns in the sense of design patterns. You don't solve a singular
problem with a monad. With something like a strategy pattern you have a
concrete problem: how do I select different potential algorithms? Monads don't
have a specific problem that they solve. Any attempt to motivate monads in
such a manner falls flat because the problem is either too general to be
motivating or too specific to apply to monads as a whole.

Instead, functors/monads/applicatives are more like a technique that can be
used to solve a wide variety of problems that all coincidentally use the same
function signature. And therefore, it's perfectly acceptable to say "I know
how monads work with Maybe and List, but not Reader" Because fundamentally,
how a Reader implements bind is in no way related to how Maybe or List
implement bind.

~~~
mavelikara
> And therefore, it's perfectly acceptable to say "I know how monads work with
> Maybe and List, but not Reader" Because fundamentally, how a Reader
> implements bind is in no way related to how Maybe or List implement bind.

If this is indeed true, what is the point of learning these "patterns"? From a
mechanical understanding of the signature of bind and unit, you'd arrive at
more sophisticated signatures, say filterM, but an understanding of what it
does will still need an understanding of the specific implementation of the
Monad instance it is being applied on. That sounds like a leaky abstraction.
If so, why bother?

~~~
bad_user
The point is that Monad, Applicative and Functor are well defined interfaces
with laws (properties) you can count on.

They are in fact much better, more precisely defined than classic design
patterns.

And in expressive programming languages (that support higher kinded types or
that at least let you encode such types) you can also describe generic code
that works over any applicative or monadic type.

Having reusable functions that work just as well on lists, maybe/option,
reader, io / promise or what have you means these type classes do a very good
job at abstracting over data types. These are the purest forms of abstraction.

And you can get syntactic sugar from the language as well. For example "for
comprehensions" in Python work via the iterator/enumerable protocol, but
that's super limiting. Haskell's "do notation" or Scala's "for comprehensions"
work on any monad instead, being much more powerful and reusable.

Unfortunately it takes an expressive language to understand and work with
these abstractions comfortably. You won't grok monads in Go or Java.

~~~
mnsc
> Having reusable functions that work just as well on lists, maybe/option,
> reader, io / promise or what have you means these type classes do a very
> good job at abstracting over data types. These are the purest forms of
> abstraction.

This is where I get kinda lost. Can you give an example of such a reusable
function that works for all these things which does something valuable?

~~~
unhammer
The simplest but still most versatile one is probably fmap (also knowns as
<$>) which lets you use a function on whatever is inside the burrito:

(+1) <$> Just 1 → Just 2 (+1) <$> getIntFromThatUserThatOnlyTypes1 → IO 2 (+1)
<$> [1,2] → [2, 3] ((+1) <$> ask) 1 → 2

Want let a function eat several burritos without making a mess? Keep your
burritos apart with < _> :

take <$> Just 2 <_> Just "abc" → Just "ab"

etc.

You can be quite productive without moving past the burrito analogy.

~~~
lmm
No. If you attempt to write monad-generic code using the burrito analogy you
will inevitably write broken code, causing serious problems for other users
and/or your future self.

Burritos may be a good analogy for some subset of monads, e.g. collections,
but they are not a good analogy for monads in general: the function you pass
to fmap may be executed now or later, zero, one, or many times, before or
after a function you pass to a later fmap.

~~~
unhammer
Nah, like any bad analogy, it can be stretched :) Once you've moved past the
"lies-to-children[1]" version, you can ascend to the real meat of the burrito:
[https://blog.plover.com/prog/burritos.html](https://blog.plover.com/prog/burritos.html)

And once you've fully grokked burritos in terms of monads, you can finally
reach that zen level where you solve your problems without even a single line
of code (because you've started working at the burrito shop instead:
[http://chrisdone.com/posts/monads-are-
burritos](http://chrisdone.com/posts/monads-are-burritos) ).

[1] [https://en.wikipedia.org/wiki/Lie-to-
children](https://en.wikipedia.org/wiki/Lie-to-children)

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

~~~
TheAsprngHacker
I am the author of this submission. I have strong opinions about functor and
monad tutorials, and here are my thoughts:

Back when I didn't understand what a monad was, I would read a bunch of
tutorials and get confused by the analogies and examples. For example, I would
get confused by comparisons to "boxes," or I would think that Maybe was the
_definition_ of a monad, or that IO was the _definition_ of a monad.

The information in the tutorial that you link seems to be all correct.
However, it's too "jumpy" for my tastes. The tutorial talks about "contexts,"
but doesn't really explain what a "context" is, except for making a comparison
to "boxes." I get that a functor is an abstract idea, so explaining it in an
understandable way is difficult. However, I wish that the article would
discuss type constructors, because the idea of mapping types to types is an
important part of the definition of functor. Without this explanation, I
imagine that the comparison to "boxes" would have given the past me the wrong
impression of what a functor is.

In my tutorial, I sought to teach the actual definition of a functor,
applicative, and monad. I explain that a functor maps types to types and
functions to functions in a way that preserves composition and identity, that
an applicative preserves the product, and that a monad is characterized by a
"join" operation that "flattens" the data and a "return" operation that "wraps
up" the data. I actually would have preferred to explain the actual category
theory, but I felt that it would be too intimidating, and so I attempted to
convey the ideas in a non-category-theory way. With my current understanding
of functor, applicative, and monad, I believe that if one doesn't learn their
actual definitions, one doesn't truly understand them. I guess that I wanted
my tutorial to be more rigorous.

However, I am not an expert on teaching, so maybe I'm taking the wrong
approach.

See my Reddit comment:
[https://www.reddit.com/r/programming/comments/cy35zz/functor...](https://www.reddit.com/r/programming/comments/cy35zz/functor_applicative_and_monad/eyshqyc/)

~~~
aargh_aargh
Thank you for taking what seems to be an interesting approach to explaining
these concepts.

> I use OCaml in this tutorial, with some occurrences of Haskell.

> My intention is for anyone familiar with the basics of typed functional
> programming to be able to follow along.

I don't know OCaml or Haskell and I got lost very early on due to the
unfamiliar syntax. Do you think your explanation would be easy to translate to
a more widely known language like Python or C?

~~~
TheAsprngHacker
_Shrugs_ Well, Python is dynamically typed and C doesn't really have good
polymorphism or first-class function support. Both polymorphism and first-
class functions (with closures) are important for understanding functors,
applicatives, and monads.

I admit that I'm more fluent in OCaml than I am in Python or C, so please
correct me if I'm mistaken.

~~~
lmm
Python and C would indeed be unsuitable. Maybe Rust (quite OCaml-like, but
with a more C-like syntax) or Java/C#/Kotlin would let you present these ideas
with a more "mainstream" syntax?

~~~
lonelappde
You can do it in Scala which has powerful type support.

The problem with other languages is that they lack the support for the higher
order types, so the implementation is dishonest or you have to build up an API
(new embedded minilanguage) to express them, which is a lot of work and a
distraction.

And, the result tends to be very cluttered with syntax junk, as you can see in
Scala and Functional Java.

Which incidentally is why people generally don't use these ideas in Java code.
You can use these design ideas in your Java architecture, but not directly
express them in your Java code.

~~~
lmm
OCaml also lacks higher-kinded types. As far as I can tell, anything you can
do in OCaml you can also do (perhaps verbosely, though less so in more recent
versions) in Java.

~~~
ernst_klim
OCaml have HKT, it's in the module part of the languiage, it's just verbose.

And OCaml's module language is much more powerful than Java, since module
language is a dependently typed language. You can't, say, pass a class
including a type for another class in java.

------
uryga
there's an equivalent definition of Applicative that may be a bit less
intimidating:

    
    
      class Functor f => Applicative f where
        unit :: f ()
        (**) :: f a -> f b -> f (a,b)
    

[source - a bit CT
heavy]([https://stackoverflow.com/a/35013667/5534735](https://stackoverflow.com/a/35013667/5534735))

(i'll be writing the second operation as `××` in some places because of HN
asterisk weirdness)

this gives us:

\- `unit`, a "template" container with a hole we can fill (by doing `fmap
(const myValue) unit`, equivalent to `pure myValue`)

\- `fa ×× fb`, to "compose" two Applicative values. this shows how, unlike
with Monads, the two computations must be "independent". for example, if IO
were only an Applicative, you could do

    
    
      print "hello" ** print "world"
    

but not

    
    
      getLine >>= \s ->
       print ("hello, " ++ s)
    

(where the second computation depends on the string we got in the first one)

it also nicely shows the two ways lists can be an Applicative - `fa ×× fb` can
be either the Cartesian product `[(a,b) | a <\- fa, b <\- fb]` or `zip a b`
(ZipList).

(also, it's kind of like a Monoid, which is neat!)

~~~
TheAsprngHacker
Huh? I cover this definition in the tutorial, when I discuss OCaml's (and+)
operator! Did I gloss over things too quickly?

~~~
uryga
oh man, i must've missed it! tbh though, i just quickly scanned through the
article to see if this needed plugging, because I remember being completely
stumped by `(<×>)`. too late to edit it now though :/ sorry!

------
hope-striker
Nice explanation of monads!

My two cents: personally, I would've started with the fact that a monad is
exactly the stringing together of functions (a -> m b), and the similarity
between monoids, monads, strings / lists and functions under composition. You
mentioned that

• (a -> [b]) is the type of non-deterministic computations

• (a -> Maybe b) is the type of fallible computations

• (a -> IO b) is the type of effectful computations

• (a -> (s, b)) is the type of computations with state s

• that the Monad instance merely specifies how to compose them

• all such composable constructs can be expressed as a monad

• do notation and list comprehensions automatically work across all of them

and, in my opinion, these are all much more powerful motivation than beginning
with a comparison to functors.

------
BucketSort
It's just mind boggling how universal these concepts are and how they show up
in surprising ways. As a recent example, I've been learning the basics of
composing music in Haskell with Euterpea[1] and wanted to make a function
which played several notes over a list of octaves to make chords. It turns out
the applicative operator was exactly the function I need to do this! It would
be hard to go into the details in just a little blurb here... but here is a
piece I made with with it[2]. And here's the line of code with the applicative
operator:

notePlayer notes octs dur = musicSeq $ (uncurry <$> notes) <*> ((, dur) <$>
octs)

Won't make much sense without context, but it's there!

[1]: [http://www.euterpea.com/](http://www.euterpea.com/)

[2]: [https://soundcloud.com/a-mathematical-way/not-enough-time-
to...](https://soundcloud.com/a-mathematical-way/not-enough-time-to-read-the-
manual)

~~~
kccqzy
I have never heard of Euterpea, nor have I seen the rest of your code, but I
suggest you refactor your slightly convoluted code like this:

    
    
        notePlayer notes octs dur = musicSeq $ notes <*> octs <*> pure dur
    

Coincidentally, this might be a testament of the power of parametric
polymorphism and equational reasoning.

~~~
whateveracct
My favorite part about Haskell is how you can know literally nothing about the
domain and make meaningful changes to programs regardless thanks to local
reasoning.

That's really one power of functor/applicative/monad - if you understand their
interfaces, you can work with new unfamiliar types that have these instances
without much effort at all.

~~~
BucketSort
It's amazing. No one could ever do something like this with an imperative
language!

~~~
whateveracct
Nobody ever said that :)

Although abstracting over this stuff isn't possible in any imperative language
unless Scala or maybe advanced C++ template count. But that isn't due to their
imperative nature exactly.

But local reasoning is very hard to count on in most other languages - that's
for sure. It's easier to just run a VM in your head.

------
larusso
I always go back to “Learn you a Haskell for Great good”.

[http://learnyouahaskell.com/functors-applicative-functors-
an...](http://learnyouahaskell.com/functors-applicative-functors-and-monoids)

------
nacc
Being someone who is still struggling with these concepts, I like this
tutorial because at least it doesn't use the common list/maybe/state to
illustrate the concepts. Somehow I feel these concepts are so abstract -
unless one is well versed in category theory, maybe only a data approach can
prevent people from overfitting these concepts to specific examples.

I would really hope to see a tutorial that have a diverse set of examples and
just fmap each example with a light explanation of say, what is a monad in
this code and what is not, and because it's a monad we can do this.

Essentially the tutorial can just train a classifier in one's head, and with a
nice set of examples maybe the brain can learn a general representation of
concepts for the classifier ...

~~~
mjlangiii
In this series functional concepts are very gently introduced. I feel like it
really appreciates the beginners starting point and assumes very little. Is
this close to what you want?

[https://egghead.io/lessons/javascript-linear-data-flow-
with-...](https://egghead.io/lessons/javascript-linear-data-flow-with-
container-style-types-box)

------
ollysb
After learning Elm for a few months I picked up a Haskell book. When I got to
the Functor, Applicative and Monad chapters I discovered that I’d actually
been using them for months without knowing it. Having had practical experience
with them the theory came as a revelation. I found that I’d developed an
intuition for them from all the concrete scenarios where I’d used them.

The Elm community goes out of it’s way to avoid talking about them because
they make learning functional programming seem far more intimidating. It’s a
very simple language though - Evan had always had beginners in mind when
desiging the language and tools. If you’re looking to improve your functional
programming skills it’s got a great learning curve.

------
hoseja
I should learn to read Haskell one of these days. Not today though.

~~~
ipnon
"Learn You a Haskell" is sparse on theoretical foundations. I made it halfway
through the book feeling like I only had a superficial understanding of the
language. If you are interested in a rigorous or systematic approach I
recommend "Haskell Programming from First Principles".

~~~
weavie
I think both are great books, but the book you choose is secondary in
importance to the amount of time you spend just bashing your head against it
until it starts to make sense.

------
voidhorse
I still find Philip Wadler’s original paper on Monads to be the clearest
explanation of the pattern and concept. It remains scoped to the usefulness of
the concept in programming, lays out very clear motivating examples, and
proceeds to implement the pattern to solve each case in a lucid and explocit
manner. I’d say the only downside is that it assumes at least some familiarity
with FP and writing more “theoretical” or academic tools like evaluators, but
all in all it’s still much clearer than the majority of garbled explanations
of monads, even those that attempt to explain the concept through its
dependencies/priors (functor/applicative).

here’s the paper:
[https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/b...](https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf)

------
moomin

        It turns out that every generic type t has a corresponding map function map : ('a -> 'b) -> 'a t -> 'b t.
    

So how about x : 'w -> Bool?

~~~
TheAsprngHacker
If there is a function `map : ('a -> 'b) -> 'a t -> 'b t`, and there exists a
function `x : 'w -> bool`, then `map x : `'w t -> bool t`. Is this what you
were asking?

~~~
antisemiotic
Think `data Foo a = Foo (a -> Bool)`, pardon my Haskell. A function `map :: (a
-> b) -> Foo a -> Foo b` is impossible, however `contramap :: (a -> b) -> Foo
b -> Foo a` is fine (just pre-compose the given function with the stored
function).

Even worse with `data Bar a = Bar (a -> a)`.

~~~
TheAsprngHacker
Thanks. I know what contravariant functors are, but I haven't used them, so I
didn't realize that was what the parent commenter was asking about.

You're right, my claim that every type 'a t has a corresponding (covariant)
functor is incorrect, and I should either take that out or mention
contravariant functors.

~~~
moomin
It’s even worse than that. Consider

    
    
        data Foo a = Foo (a -> a)
    

This admits neither Functor nor Contravariant. Sadly all you can say is “If
you can map, it’s a functor.”

I always end up finding out more about any subject I actually publish a post
about when people read it...

~~~
TheAsprngHacker
Thank you for pointing out contravariant functors; I have fixed that sentence
in my post and credited you.

~~~
moomin
Oh that’s much more extensive. Like the link to Julie’s work.

------
leshow
> According to currying, 'a -> 'b -> 'c is interchangeable with 'a * 'b -> 'c.
> (The fancy math word for this interchangeability is "isomorphism.")

I thought isomorphism was when a function was reversible. I didn't think it
had anything to do with currying.

~~~
Iceland_jack
It means there are two functions going between them that witness the
isomorphism.

The witnesses in Haskell are the higher-order functions (they transform
functions) `{,un}curry`:

    
    
        curry :: ((a, b) -> c) -> (a -> b -> c)
      uncurry :: (a -> b -> c) -> ((a, b) -> c)

------
_bxg1
I've always understood intuitively the existence of .map(), .reduce(), and
.flat() in JavaScript, but .flatMap() felt like a weirdly arbitrary
combination of two of them. Now it makes sense!

------
foobar_
Maths is what you get when you limit all your variable names to single
letters.

~~~
kybernetikos
And make symbols mean different things in different situations, and sometimes
reuse symbols for the same thing, and sometimes make sin(x+3) mean the
variables s * i * n * (x+3) and sometimes mean taking the sine of x+3.

Mathematical language is worse specified than markdown and has a huge amount
of ambiguity and requirement that you understand the context that you're
working in.

It works fairly well for mathematicians, in terms of being concise to express
complex ideas on a blackboard, but overall it's a mess.

~~~
foobar_
And the naming convention is absolutely horrid. It seems the main criteria is
.. it should sound clever.

------
harry8
Pretty weird that a comment linking a chapter from an oft referred to haskell
text is a dead comment here. Surely if the alternate explantion in "Learn You
a haskell for great good" is somehow sub-optimal it would be better to explain
how rather than kill the comment inside 10 minutes?

You see this sort of thing from language warriors fighting silly wars but,
yeah, what's wrong with Learn You a Haskell? Why must it be fought and
suppressed immediately? Crazy...

~~~
mkl
It was probably automated. larusso has only made two comments ever, both
containing links, so that probably triggered the spam detectors. You can
manually resurrect for accidentally dead comments like this by clicking on the
comment's time and then clicking "vouch".

~~~
larusso
I’m personally more the consumer type here at hacker news.

