Skip to content

Functors and monads: a twofer! Scary words in programming episode 3

Welcome to episode 3 of Scary Words in Programming! (with updates from comments)

scary-words-in-programming-with-pam

Functors & Monads (a twofer!)

Yesterday, my new buddy (from StrangeLoop), Brian Lonsdorf and I did a Hangout on Air in which he explained monads to me (that was the deal). But first, he explained functors! Thus, you all get a twofer post.

However, sad news. You get what you pay for with free things, and apparently Youtube/Hangouts+ has killed my recordings “because they were too long,” and it’s a known bug in Hangouts, according to the product forums. I’m quite disappointed, and still trying to hunt them down, so I’ll let you know if I find them.

In the meantime, more on me to somehow explained what I learned in words.

So, along the conversation and demos Brian walked me through, I learned:

A functor is a function data type that leverages map().

A monad leverages flatmap() (or chain() or other ways of writing that), working within the same type.

Ripped from the comments (because Michael Ficcara is awesome):

Functor and monad are both names for interfaces. A data type that conforms to the functor interface must implement a map function. A data type that conforms to the monad interface must already be a functor (specifically an applicative functor) and must implement a bind/flatMap/chain function and a return/unit/of function.

As we discuss in the video I don’t have, the most difficult thing about these concepts is that they’re abstract. Seeing it in action can help. In the gist, I created (from memory, may contain errors) what Brian walked me through, where he showed a map function that returns a container, whereas flatmap does not include that container (it is a _flat_ map).

Regular map is awesome, but flatmap gets us to chaining, which can be extra awesome. Update: Check out Michael’s gist that implements the Maybe monad, which is more useful than this identity monad.

Reminder: what does map do? It performs an operation on a value in place in the same kind of container*. Anything that does that is a functor. Meanwhile, flatmap/monad-like things do the same thing, but they don’t modify the container, i.e. it’s type safe.

In Brian’s examples, he talked about how by using data structures such as these, you could, for example, check ONCE if a value is as expected, and then chain onward. If the value returns a value you don’t want to continue operating on, you stop right there, and don’t have to build more checks into your system.

For people who write JavaScript, that’s a big deal. Pretty much every function needs to have undefined checks and = foo || {} statements around if you don’t build in a type safe manner. So JavaScripters, listen up! Brian suggests checking out the folktale libraries if you’re looking for some functional goodness, and check out the Maybe and Either monads.

I really hope I can rescue the video, but if not, hopefully this makes monads and functors a little less scary!

*Updated in response to comment. ex. [1,2,3].map(…) returns an array that has those modifications performed on it, but the original [1,2,3] did not change

6 Replies to “Functors and monads: a twofer! Scary words in programming episode 3”

  1. I’ve made a few corrections to the example in this fork of the gist: https://gist.github.com/michaelficarra/cd66c547cbd0d4daa9e1. Remember that flatMap (or bind or >>= or whatever you prefer to call it) must produce the container itself, even though its input is the value that is held within it. This is very very important, as it is this restriction that makes the monad abstraction so powerful.

    I think a motivating example might be in order. What you’ve implemented is the Identity monad. From the Haskell docs,

    The Identity monad is a monad that does not
    embody any computational strategy. It simply
    applies the bound function to its input without
    any modification. Computationally, there is no
    reason to use the Identity monad instead of the
    much simpler act of simply applying functions
    to their arguments.

    So let’s look at the Maybe monad instead. I’ve written a short example of a possible implementation in JavaScript here: https://gist.github.com/michaelficarra/ecdee4b556bcb5c6969a. There are of course others all over github. Hopefully my example shows how the Maybe monad will propagate a failure safely to the end of the series of operations you want to perform. Remember, each of these operations may fail, but none of them have to worry about handling failure from the previous operations. This gives us very DRY and composable code.

    Also, regarding “what does map do? It performs an operation on a value in place”. Not true: map produces a new functor that has reconstructed the same kind of container using the result of applying the function to the value(s) it contains. That allows for immutability, but has little to do with type safety. Type safety guarantees we aren’t trying to map a banana over our functor instead of a function (and similar nonsensical things).

    1. But is map a functor itself? No using words themselves in definitions. Definitions of functors include the word mapping, so what’s map?

      You’re right, it’s not in place, because it’s not changing value (immutability), more that it looks like it’s in the same place because it’s in the same kind of container. (yes?)

      1. map is a higher-order function. It takes a function from A to B and a functor containing zero or more values of type A and produces the same kind of functor except containing one B value for every A value in the input. Note that A and B may be the same type.

        As an example, lists are functors, so mapping (+ 1) over [0, 1, 2] will produce [1, 2, 3]. Try it out in JS: `[0, 1, 2].map(function(x){ return x + 1; })`. More interestingly, a future (or promise) is also a functor. If we have a future that will produce some integer for us in the future, we can map a function that turns integers to their string representation over the future and produce a new future that will produce a string for us in the future. Again, here’s an example in JS:

        futureInt.then(function(x){
        // x is an integer here in the future
        });

        futureInt.map(function(x){
        // convert int to hex representation
        return x.toString(16);
        }).then(function(x){
        // x is a string of hex characters here in the future
        });

        Remember, futureInt.map will produce a new future, which we may have called futureHex. It will not change how futureInt works.

        1. So what’s your take on these statements then?:

          A functor is a function that leverages map().

          A monad leverages flatmap() (or chain() or other ways of writing that), working within the same type.

          You’re saying a functor is an object or rather, data type, on which we can map?

          1. Functor and monad are both names for interfaces. A data type that conforms to the functor interface must implement a map function. A data type that conforms to the monad interface must already be a functor (specifically an applicative functor) and must implement a bind/flatMap/chain function and a return/unit/of function. From the Haskell source:

            class Functor f where
            fmap :: (a -> b) -> f a -> f b

            class Monad m where
            — | Sequentially compose two actions, passing any value produced
            — by the first as an argument to the second.
            (>>=) :: forall a b. m a -> (a -> m b) -> m b
            — | Inject a value into the monadic type.
            return :: a -> m a

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.