Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS Monad unit function

I'm struggling with understanding the unit function in JavaScript. Particularly because the thing that made me 'get' monads (or at least I thought) was the Promise object, and how then always returns a new Promise, regardless of what function you pass to then, which, to my knowledge is equivalent to bind or >>= in haskell. This totally makes sense to me, because it ensures that all of your functions are executed in the 'monad universe', so to speak.

What's tripping me up is the 'Monads and Gonads' talk by Douglas Crockford. In his implementation, bind directly returns the result of the transform function, without checking if the result itself, is a monad. This clashes with Promises's then method, as then ALWAYS returns a new Promise.

One thought was the lift method. His implementation does ensure that 'lift' will always return a monad, and maybe then was lifted on to Promise. However, this would mean that then !== bind, and that Promise has an internal bind somewhere.

My intuition is that there should at least be some sort of type check in the bind function that checks the result of the transformation, and allows a resulting monad to be let through, but will intercept non-monads and pass them through unit again, like 'lift' does.

*EDIT
Also, I'm under the impression that then is equivalent to bind, flatMap, >>= because it has the ability to unwrap other monads, including different ones and ones of it's own type. While looking in to some category theory references in JavaScript, flatMap was used to map over a set of nested arrays, and then flatten them by one dimension. This fits with how then will wait for other promises you give it. But doesn't seem to match up with the original implementation mentioned above. I feel lost.

Can anyone with more FP experience shed some light on what I'm missing, or am I just too off, and need to start from the beginning?

Some code examples...

// Crockford's 'bind'
monad.bind = function(transform) {
  // value was passed in through the unit constructor
  return transform(value);  
}

My trouble area

// Set the 'isMonad' prop to be true, for all
// monads made with the MONAD macroid
monad.isMonad = true;

// shouldn't this ALWAYS return a monad?
monad.bind = function(transform) {
  var res = transform(value);
  return ( res && res.isMonad ) ? res : unit(res);
}

NOTE I know i'm not using the final version of his implementation in full, I'm just focusing in the bind method in particular.

The full implementation can be found at

https://github.com/douglascrockford/monad/blob/master/monad.js

Update

After doing some more research, >>= is not required to return a Monad instance. Bergi's comment shed some light on how Promise.prototype.then is overloaded and acts as a different function depending on what you resolve it with.

Also, a lot of things started to click when I took a step back and looked at how Monads are different from regular functors. The details are still a little fuzzy, but I think I get the big picture.

A few good references that helped clear the haze,

This one is highly recommend for a high level overview, in human words
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
Don't let the pictures fool you, this one was like gold for me. Not in JavaScript, but still very informative about the overall concepts.

Also, this YouTube series on Category Theory in JavaScript
https://www.youtube.com/watch?v=-FkgOHvNAU8&list=PLwuUlC2HlHGe7vmItFmrdBLn6p0AS8ALX&index=1

This YouTube series called 'Fun Fun Function' is wonderful, the host is one of the best teachers I've found online. This video is about monads and was suggested by MrE.
Highly recommended!.

https://www.youtube.com/watch?v=9QveBbn7t_c&app=desktop

Those two references specifically did wonders for me. Hope that helps everyone else as well.

like image 831
Dustin Stiles Avatar asked Mar 19 '16 08:03

Dustin Stiles


People also ask

What is monad in JS?

A monad is a way of composing functions that require context in addition to the return value, such as computation, branching, or I/O. Monads type lift, flatten and map so that the types line up for lifting functions a => M(b) , making them composable.

Is a monad a function?

In functional programming, a monad is a software design pattern with a structure that combines program fragments (functions) and wraps their return values in a type with additional computation.

What is a functor JS?

A functor data type is something you can map over. It's a container which has an interface which can be used to apply a function to the values inside it. When you see a functor, you should think “mappable”. Functor types are typically represented as an object with a .

Is map a monad?

Map is not one of the defining properties of monads, however, because it's technically just a special case of FlatMap. A lifting function like Unit will wrap its object in a container, even if that object is itself the same type of container.


1 Answers

I don't quite get what your question is, but I'd assume something like:

what is the correct definition of a Monad and it's two methods in terms of JS?

In Haskell terms (taken from https://en.wikibooks.org/wiki/Haskell/Understanding_monads), it's simple:

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

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

For JavaScript terms, look no further, the short and simple answer is here https://github.com/fantasyland/fantasy-land#monad along with other connected FP definitions.

Few words on the methods:

  1. One thing is unit (return in Haskell) must produce a monad (not precisely monad, but for sake of argument...), since it's like a constructor that puts a value inside the container. Array.of() is one example, jQuery() is another, and of course new Promise() as well.

    In the Fantasy Land Specification this is the of() function/method.

  2. The second one is important only because Haskell uses a definition of monad having unit and bind while others (fmap,join) are inferred form them.

    The Haskell's bind is named chain in the Fantasy Land Specification becausebind is defied on Function.prototype in JavaScript, so someone thought chain would be close enough.

    And the reason why bind i.e. chain "must" return a monad of the same type is because of (>>=) :: m a -> (a -> m b) -> m b. In short, the Haskell's bind function must only accept a function that returns a monad (this part a -> m b), so you're getting the result of it.

  3. The Haskell's then

    is a mere convenience

    and

    sequences two monadic actions when the second action does not involve the result of the first, which is common for monads like IO.

In practice:

  1. It may irk you in JS, since there is no strict type enforcement, that you can always not follow the rules and return whatever you want from a Promise, for example, thus breaking the .then() chain of said promise(s).

  2. Some monads like jQuery have "lifted" functions as methods that always return jQuery i.e. of the same type thus "protecting" the ability to chain.

like image 165
Azder Avatar answered Oct 07 '22 16:10

Azder