
Monads in JavaScript - eatonphil
https://curiosity-driven.org/monads-in-javascript
======
ghik
PLT_HULK would have something to say about it :)
[https://twitter.com/plt_hulk/status/397822619736870912](https://twitter.com/plt_hulk/status/397822619736870912)

~~~
mlitchard
I feel like this rush to adopt functional language features in non-functional
languages is like deciding to shoehorn a jet engine onto your hot-air balloon.
Yes, it can be done but ... see PLT_HULK.

~~~
_davidchambers
These two functions are equivalent:

    
    
      const f1 = function(s) {
        let data;
        try {
          data = JSON.parse(s);
        } catch (err) {}
    
        if (data != null &&
            data.a != null &&
            data.a.b != null &&
            typeof data.a.b.c === 'string') {
          let x = parseFloat(data.a.b.c);
          if (x === x) {
            return x;
          }
        }
        return null;
      };
    
      const f2 =
      R.pipe(S.parseJson,
             R.chain(S.gets(['a', 'b', 'c'])),
             R.filter(R.is(String)),
             R.chain(S.parseFloat),
             S.fromMaybe(null));
    

Note that in the first function we have an unsafe expression,

    
    
      data.a.b.c
    

which we must guard with null/undefined checks. Unsafe expressions can easily
get out of sync with their corresponding guards.

We can apply functional programming concepts to JavaScript code to obviate the
need for null/undefined checks. The fact that we can't have _all_ the benefits
of FP in JS shouldn't dissuade us from taking advantage of the ideas which are
applicable.

~~~
comex
Less as a real argument than as devil's advocate, here's a shorter non-library
version:

    
    
       function f1(s) {
          try {
             let data = JSON.parse(s);
             let x = parseFloat(data.a.b.c.substr(0));
             if (x === x)
                return x;
          } catch(e) {}
          return null;
       }
    

...ok, it's a bit golfy, and in JavaScript you don't get much control over
exception catching. But in general, exceptions can be used like an implicit
Maybe/Either monad, just as mutability is an implicit IO monad.

(Also, you picked an odd task - it's unusual to expect a float to be
represented as a string in a JSON object, and a check that rejects NaN but
accepts weird floats like Infinity is not terribly useful.)

~~~
_davidchambers
It's a contrived example, certainly.

Your counterexample is informative. I hadn't thought of exceptions in this
way. The downside of putting everything in a `try` block, of course, is that
we'll potentially catch exceptions arising from a bug in our code which
_should_ crash our program.

------
debacle
I detest how everyone always takes monads, a quite simple concept, and
completely butchers the explanation. I think it's likely in large part a
reason for a lack of their acceptance and use.

~~~
theophrastus
I'd say you're now almost guilt-edged required to provide us with your simple
explanation here. But i'll warn you, i've been nicely flummoxed by competing
'simple' explanations in this forum previously: "A Monad is an object whose
methods return monads.", "They're simply a highly specific way of chaining
operations together.", "You know how jQuery methods can string out multiple
methods with periods? That's what monads are.", "Monads are merely an analogy
to control flow what abstract data types are to data."

~~~
anon4
Haskell is a pure language. Purity is a concept that is absolutely bizarre to
the real world, where adding two numbers together results in tons of side
effects (load into registers, oops, have to load cache lines, alter memory,
raise the ambient temperature, etc. etc.). However, the idea of pure functions
is useful. So we make our basic program blocks from pure functions that don't
"do" stuff, but just transform values.

However, we must chain these into useful programs somehow. Monad is the type
name chosen in Haskell for the construct that does this. It's kind of simple
if you work it backwards from the syntax. You want to be able to say

    
    
        do
          a <- readInt
          b <- readInt
          add a b
          print
    

The first two can fail, rely on side effects and the last one is purely a side
effect. The only pure function is add, which can't work if a or b aren't
there. Monads serve as a way to represent the real-world-ness without it
seeping into the add function. You can think of each line as a closure getting
the next value from the previous line. So readInt returns a Maybe Int. If it
returns a Nothing, the next closure is simply skipped. You can insert a check
after the add, if you want to, to see if you got Nothing and alert the user,
etc. However, the basic idea is that the program does not crash with a
NullPointerException. We know what to do if we get Nothing. The add function
doesn't need to concern itself with that.

Hence, the Monad. It wraps a real world concern around a type (it has a type
constructor). You must be able to make one from a concrete value that is
unburdened with exceptional state (it has the unit function). You must be able
to call pure functions on these monadic values without the side state seeping
into the function (it has the bind function).

Edit: the reason you don't see Monads in other languages is because they're
not essential to them. You can do stuff in C without Monads. To Haskell,
Monads are like the Higgs boson. It's how you bind stuff together.

~~~
nemothekid
Your explanation makes sense, but I don't see how are Monads different from
Rust's Option Type (or Java's Optional Type?), or any other nullable type?

~~~
tel
"Monad" is more like an adjective. "Option" is a monad, but, interestingly,
the union type (A | null) is _not_.

~~~
dllthomas
As I understand it, this is because nullability (typically?) doesn't nest and
so can't be a functor (with the whole of the types of the language as domain).
If you can't wind up with m (m a), then there is no place for join.

~~~
tel
Yeah, exactly. The other way to see it is to note that `Nothing(null)` is a
perfectly reasonable Javascript expression which ought to mean something
different from `null` alone. If that cannot be true then you don't actually
have parametricity.

------
plaid_dev
We've leaned on this functional JS library to make JS more type safe
([https://github.com/plaid/sanctuary](https://github.com/plaid/sanctuary)).
Heavily borrows Monads and other FP concepts.

~~~
soroushjp
Very powerful addition to JS. Definitely using this in my next project.

------
tel
Here's a thing I wrote when this was submitted to Lobste.rs: an implementation
of the continuation monad which was elided.

[https://lobste.rs/s/r5oeqr/monads_in_javascript/comments/sxt...](https://lobste.rs/s/r5oeqr/monads_in_javascript/comments/sxtbia#c_sxtbia)

------
erikpukinskis
Am I crazy that I never understood the point of talking about monads? It just
seems like a trivial pattern. It's like having a Subroutine Pattern, or a
Variable Assignment Pattern. I'm all for investing in better primitives, but
do these kinds of libraries really help vs implementing them from scratch with
language primitives inside your (inevitably more complex) domain code?

~~~
5outh
The big problem with monad tutorials is that you can't talk about them without
understanding the motivation behind them, and that comes from purely
functional languages (i.e. Haskell). When you're not allowed to touch IO, use
`null`, exceptions don't exist, there is no global state, and all of these
things have a common pattern to them, then you can motivate their use and talk
about them. In my opinion, there isn't much of a point in talking about them
without those things. There are some neat tricks (`flatMap` etc...) but
without proper motivation, it's not likely to stick, and the idea seems
relatively useless.

~~~
_ak
So what you're saying is that these purely functional languages are so
restricted that if you want to do anything useful in them, you _need_ this
concept that nobody on the internet is able to actually explain.

Makes me want to use purely functional languages even less.

~~~
codygman
> So what you're saying is that these purely functional languages are so
> restricted that if you want to do anything useful in them, you _need_ this
> concept that nobody on the internet is able to actually explain.

What do you define as useful? Reading a file and taking the first line maybe?
Here's what you need to know:

Start ghci (I'm using stack[0], if you don't have stack you can also just use
`ghci` here).

    
    
        cody@cody-G46VW:~$ stack ghci
        Using resolver: lts-2.16 from global config file: /home/cody/.stack/global/stack.yaml
        Configuring GHCi with the following packages: 
        GHCi, version 7.8.4: http://www.haskell.org/ghc/  :? for help
        Loading package ghc-prim ... linking ... done.
        Loading package integer-gmp ... linking ... done.
        Loading package base ... linking ... done.
        Prelude> import Control.Applicative ((<$>))
        Prelude Control.Applicative> readFile "/etc/issue"
        "Ubuntu 15.04 \\n \\l\n\n"
        Prelude Control.Applicative> 
    

Now we are going to use the $ sign which allows you to avoid extra
parenthesis.

    
    
        Prelude Control.Applicative> let add x y = x + y
        Prelude Control.Applicative> let subtract x y = x - y
        Prelude Control.Applicative> add 5 (subtract 3 5)
        3
        Prelude Control.Applicative> -- now using $
        Prelude Control.Applicative> add 5 $ subtract 3 5
        3
        Prelude Control.Applicative> -- what's the point of me teaching you the $ operator?
        Prelude Control.Applicative> -- it's similar to one we'll use to apply our lines function to what readFile returns
    
    

How can we split the file by newline?

    
    
        Prelude Control.Applicative> (fmap lines (readFile "/etc/issue"))
        ["Ubuntu 15.04 \\n \\l",""]
    
    

From there, how can we get the first item?

    
    
        Prelude Control.Applicative> (fmap head (fmap lines (readFile "/etc/issue")))
        "Ubuntu 15.04 \\n \\l"
    

The duplication of fmap is getting kind of tedious to me. We can use function
composition to avoid it. I'll use the add and subtract function definitions
from above, but I'm sure it's obvious what those do.

    
    
        Prelude Control.Applicative> let addFiveSubtract3 x = (add 5 . subtract 3) x
        Prelude Control.Applicative> addFiveSubtract3 0
    

Now you know (hopefully) how function composition works, how can you use it
for real stuff?

    
    
        Prelude Control.Applicative> -- back to reading files and stuff
        Prelude Control.Applicative> fmap (head . lines) (readFile "/etc/issue")
        "Ubuntu 15.04 \\n \\l"
    

Remeber that seemingly pointless detour to teach you what $ does? Let's try
using that.

    
    
        Prelude Control.Applicative> fmap (head . lines) $ readFile "/etc/issue"
        "Ubuntu 15.04 \\n \\l"
    

There is an infix version of fmap called <$> which you might notice resembles
$ with the addition of square brackets, kind of like it's in a box or
something. Try removing `fmap` and replacing `$` with `<$>`. I'll wait here.

....

....

....

....

....

Done? Alright, here is the solution just in case:

    
    
        Prelude Control.Applicative> (head . lines) <$> readFile "/etc/issue"
        "Ubuntu 15.04 \\n \\l"
    

We can actually get rid of the parenthesis on the left hand side of <$> like
so:

    
    
        Prelude Control.Applicative> head . lines <$> readFile "/etc/issue"
        "Ubuntu 15.04 \\n \\l"
    

Now how can we print it out? Well, let's see what our type is:

    
    
        Prelude Control.Applicative> :t head . lines <$> readFile "/etc/issue"
        head . lines <$> readFile "/etc/issue" :: IO String
    

Something to know about Haskell is that it's okay to be naive and pick
functions whose type signatures look like they do what you want. There are
even search engines[1][2] that take type signatures and give you functions.

So what is the type of putStrLn?

    
    
        Prelude Control.Applicative> :t putStrLn
        putStrLn :: String -> IO ()
    

How could we go from `IO String -> IO ()`? Well, IO is a Monad (thing which
follows some rules as defined by typeclasses). A monad defines a few things as
you can see here:

[http://hackage.haskell.org/package/base-4.8.0.0/docs/Prelude...](http://hackage.haskell.org/package/base-4.8.0.0/docs/Prelude.html#v:-62
--62--61-)

But remember, we aren't interested in intimately understanding all that
stuff... just in getting our functionality working. Maybe later we can circle
back and figure stuff out at a deeper level.

The first function defined is >>=, whose type is:

    
    
        (>>=) :: Monad m => m a -> (a -> m b) -> m b
    

What was that type we needed again? Let's compare these two:

    
    
        (>>=) :: Monad m => m a -> (a -> m b) -> m b
    

If we look at the type of our function getting the first line of a file:

    
    
        head . lines <$> readFile "/etc/issue" :: IO String
    

We see that it can fit into the m a part of this:

    
    
        (>>=) :: Monad m =>  m      a    -> (a -> m b) -> m b
                             IO   String
    

Keep in mind that m means Monad, as is specified in the typeclass constraint
to the left of `=>`.

Now we can specialize our type signature to:

    
    
        (>>=) :: Monad m => IO String -> (String -> IO b) -> IO b
    

What's an `IO b`? `a` meant anything so what does `b` mean? `b` actually means
anything too, but a was already taken in the earlier part of the type
signature.

If we look at the type of our putStrLn function:

    
    
        putStrLn :: String -> IO ()
    

You might notice it fits into the second part of our new specialized type
signature:

    
    
        (>>=) :: Monad m => IO String -> (String -> IO b) -> IO b
                                          String -> IO ()
    

aside: () is kind of like void, at least that's how I think of it. It's actual
name is unit.

So our full function is now:

    
    
        (>>=) (head . lines <$> readFile "/etc/issue") putStrLn
    

Let's try it out:

    
    
        Prelude Control.Applicative> (>>=) (head . lines <$> readFile "/etc/issue") putStrLn
        Ubuntu 15.04 \n \l
    

Cool, now notice that I put parenthesis around the infix function to make it a
prefix function. Let's take those parenthesis off and use it as a proper infix
function:

    
    
        Prelude Control.Applicative> (head . lines <$> readFile "/etc/issue") >>= putStrLn
        Ubuntu 15.04 \n \l
    

Take out superfluos parens:

    
    
        Prelude Control.Applicative> head . lines <$> readFile "/etc/issue" >>= putStrLn
        Ubuntu 15.04 \n \l
    

There is a flipped version of >>= called =<< that is easier to read in this
case to me:

    
    
        Prelude Control.Applicative> putStrLn =<< head . lines <$> readFile "/etc/issue" 
        Ubuntu 15.04 \n \l
    
    

We could have also used do notation and the `<-` or "draw from IO to variable
on the left, taking appropriate actions as defined by the Monad context you
are in in the case of a failure". So if you are in the Maybe monad's context,
a failure will cause a break (using imperative terms) and return Nothing.

The example using do notation:

    
    
        Prelude Control.Applicative> putStrLn myLine
        Ubuntu 15.04 \n \l
    

There, now you know how to use monads and functors to some degree for a real
problem and know the minimum knowledge (as far as I can tell) necessary. Why
do all of this just to read the first line of a file?

Now that you know how the machinery works it's pretty simple and gets you
composability and referential transparency.

Want to know more about Functors and Monads (maybe even applicatives O_o)?
Check out Functors, Applicatives, And Monads In Pictures[3].

0:
[https://github.com/commercialhaskell/stack](https://github.com/commercialhaskell/stack)

1: [https://www.haskell.org/hoogle/](https://www.haskell.org/hoogle/)

2: [http://hayoo.fh-wedel.de/](http://hayoo.fh-wedel.de/)

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

~~~
_ak
Dude, if you wanted to open a file and read the first line, then just do so.
What you're doing is try to derive this simple task in a cery complicated
matter full of weird symbols that, even if you don't believe it, are not
entirely obvious. People who want the first line of a file want something that
expressed that in an obvious matter, not with a baggage of functional
programming theory.

~~~
codygman
> Dude, if you wanted to open a file and read the first line, then just do so.

After knowing how the Monads and Functors work, I can:

    
    
        putStrLn =<< head . lines <$> readFile "/etc/issue"
    

There are advantages to separating pure and impure functions. Pure functions
can be tested more aggressively with property testing for instance. They can
also be optimized more agressively.

Most of the errors in software in my experience come from impure functions, so
handling it in a principled way seems to reduce bugs.

> People who want the first line of a file want something that expressed that
> in an obvious matter

I feel like you mean to say "want something that expressed that in a familiar
matter". The Haskell definition I used at the beginning of this comment is
obvious to someone familiar with the basics of the programming language.

> not with a baggage of functional programming theory.

What functional programming theory? To put this into OOP terms this is just
working with well defined Objects that adhere to an interface and whose
failures are encapsulated in a sensible way.

edit: I agree it's harder to see or explain these advantages. Please give the
tutorial I posted a quick once-over and let me know what you think. If you
have specific criticisms about smaller and more specific pieces I think we
could have good discussion.

To be honest I'm constantly re-assessing whether it's worth having to deal
with the "baggage" of Haskell as you refer to it. However I go back to using
other less principled languages and things simply don't ever seem to work out
as well.

------
nextstepguy
Taking that (free) class on coursera was an eye opener on all the things that
are discussed in this article. Highly recommended:

[https://www.coursera.org/course/reactive](https://www.coursera.org/course/reactive)

~~~
dominotw
This looks awesome. I wish there was way to watch the videos of the course.

~~~
gmjosack
I was able to "join" the course for free and all of the videos are available.

------
krat0sprakhar
Love Javascript and want to learn FP? Checkout this book -
[https://github.com/DrBoolean/mostly-adequate-
guide](https://github.com/DrBoolean/mostly-adequate-guide).

I stumbled upon it last week and I've found it to be quite approachable
(although I haven't yet read the section on Monads).

~~~
findjashua
search in repository + pattern matching --> no results

:(

~~~
krat0sprakhar
Not sure I understand - are you looking to learn about 'pattern matching' in
Javascript and couldn't find anything in the book?

~~~
findjashua
correct

~~~
weavie
[https://github.com/natefaubion/sparkler](https://github.com/natefaubion/sparkler)

------
eatonphil
I came to an understanding of monads the other day thinking about fail-and-
reverse-on-error applications like messing with the filesystem. I have an
example of this (and a simple implementation in OCaml) if it helps you [0].
After my "breakthrough", I've been seeing monads everywhere; everything is a
nail.

[0] [https://github.com/eatonphil/monad](https://github.com/eatonphil/monad)

------
dschiptsov
In a strict (as opposed to lazy) non-functional language monads make no sense.
Write two statements which uses a temp variable on the same line, separated by
a semicolon - this is your monad.

~~~
crimsonalucard
No way. While I do agree monads don't make sense in some imperative languages,
writing two statements on the same line is not a monad.

------
crimsonalucard
I recently grokked monads, only to realize that monads are just a special case
of something more general called arrows. waah.

------
amyjess
Well, those are some fancy ligatures.

Wonder why the author enabled st but not ct, though...

