A little thread on monads...
(please don& #39;t get scared by the terms and run away! give it a shot!)
(please don& #39;t get scared by the terms and run away! give it a shot!)
A monad is a container around a value (like a number, string, or func) that gives that value special additional behavior it wouldn& #39;t have otherwise. For example, it helps that monad interact predictable with other monads.
In that way, it& #39;s like a Collection or Data Structure.
In that way, it& #39;s like a Collection or Data Structure.
Which behavior? Depends on what you& #39;re trying to do.
Here& #39;s an example you already will recognize:
arr = [ 1, 2, 3 ];
inc = v => v + 1;
http://arr.map"> http://arr.map (inc); // [2, 3, 4]
Here& #39;s an example you already will recognize:
arr = [ 1, 2, 3 ];
inc = v => v + 1;
http://arr.map"> http://arr.map (inc); // [2, 3, 4]
An array is a container holding multiple values. `map(..)` applies a func to all of them, and produces a new array container holding all the new values. You& #39;ve almost certainly done that in code.
Turns out, this behavior is called "Functor". I know, it& #39;s a funny/scary word.
Turns out, this behavior is called "Functor". I know, it& #39;s a funny/scary word.
But we say an array is a functor because you can take one array and map it to another array.
Functor is also one of the common behaviors of monads.
A monad generally holds a single value, but a functor means it has a .map(..) call that can produce a new monad w/ the new value.
Functor is also one of the common behaviors of monads.
A monad generally holds a single value, but a functor means it has a .map(..) call that can produce a new monad w/ the new value.
So...
three = Just(3);
inc = v => v + 1;
http://three.map"> http://three.map (inc); // Just(4)
That& #39;s not so scary, right? Just is a functor monad so we can map to a new Just monad with a different value.
three = Just(3);
inc = v => v + 1;
http://three.map"> http://three.map (inc); // Just(4)
That& #39;s not so scary, right? Just is a functor monad so we can map to a new Just monad with a different value.
Another behavior you often have used with arrays (and strings, etc) is concatenating them together, right?
arr = [ 1, 2, 3 ];
arr2 = [ 4, 5, 6 ];
arr.concat(arr2); // [1,2,3,4,5,6]
This kind of behavior in monads is called "Concat(able)".
arr = [ 1, 2, 3 ];
arr2 = [ 4, 5, 6 ];
arr.concat(arr2); // [1,2,3,4,5,6]
This kind of behavior in monads is called "Concat(able)".
For our Just monad, it can also be given the concatable behavior:
arr = Just([ 1, 2, 3 ]);
arr2 = Just([ 4, 5, 6 ]);
arr.concat(arr2); // Just([1,2,3,4,5,6])
OK, not so bad, right?
arr = Just([ 1, 2, 3 ]);
arr2 = Just([ 4, 5, 6 ]);
arr.concat(arr2); // Just([1,2,3,4,5,6])
OK, not so bad, right?
Yet another kind of behavior you might like is for operations (like map(..), etc) to run ONLY if the value in the monad is non-empty, but skip the operations in the case of like null or undefined.
This is often what the Maybe monad is used for.
This is often what the Maybe monad is used for.
A Maybe is special in that it holds either a Just (with a non-empty value) or a Nothing (with no value at all).
You& #39;d likely use a helper, like Maybe.from(..), to automatically create one or the other depending on the emptiness check.
You& #39;d likely use a helper, like Maybe.from(..), to automatically create one or the other depending on the emptiness check.
Another thing you might want to do with a Maybe is to effectively use an `if` like conditional to either retrieve the non-empty value, or substitute some sort of default.
One way of doing that is with a monad behavior called Foldable.
One way of doing that is with a monad behavior called Foldable.
Ex:
id = v => v;
function printGreeting(name) {
msg =
Maybe.from(name)
.map(n => `Hello, ${n}!!`)
.fold(
() => "Is that a ghost!?",
id
);
console.log(msg);
}
printGreeting("Kyle"); // Hello, Kyle!!
printGreeting(); // Is that a ghost!?
id = v => v;
function printGreeting(name) {
msg =
Maybe.from(name)
.map(n => `Hello, ${n}!!`)
.fold(
() => "Is that a ghost!?",
id
);
console.log(msg);
}
printGreeting("Kyle"); // Hello, Kyle!!
printGreeting(); // Is that a ghost!?
Let& #39;s break that down..
1. Maybe.from(..) checks a value to see if it& #39;s empty or non-empty, and creates either a Maybe:Just or Maybe:Nothing
2. .map(..) runs only for Maybe:Just but is skipped for a Maybe:Nothing; if it runs, it creates a new Maybe:Just holding the new greeting
1. Maybe.from(..) checks a value to see if it& #39;s empty or non-empty, and creates either a Maybe:Just or Maybe:Nothing
2. .map(..) runs only for Maybe:Just but is skipped for a Maybe:Nothing; if it runs, it creates a new Maybe:Just holding the new greeting
3. .fold( fn1, fn2 ) runs fn1 in the case of Maybe:Nothing and runs fn2 in the case of Maybe:Just. In both cases, whatever those functions return just comes out directly (not wrapped in another monad).
4. Once we have `msg` as a string, we just console.log(..) it.
4. Once we have `msg` as a string, we just console.log(..) it.
OK, interesting! We& #39;re already doing some cool stuff, with just a few of the most basic concepts of monads.
I hope *MAYBE* by now you& #39;re SLIGHTLY less nervous or confused by monads! ;-)
I hope *MAYBE* by now you& #39;re SLIGHTLY less nervous or confused by monads! ;-)
If you followed along and you& #39;re intrigued, check out my Monio library: https://github.com/getify/monio
And">https://github.com/getify/mo... for more reading on monads:
#appendix-b-the-humble-monad">https://github.com/getify/Functional-Light-JS/blob/master/manuscript/apB.md/ #appendix-b-the-humble-monad
(that& #39;s">https://github.com/getify/Fu... appendix B of my @FLJSbook, which I also encourage you to read all of!)
And">https://github.com/getify/mo... for more reading on monads:
#appendix-b-the-humble-monad">https://github.com/getify/Functional-Light-JS/blob/master/manuscript/apB.md/ #appendix-b-the-humble-monad
(that& #39;s">https://github.com/getify/Fu... appendix B of my @FLJSbook, which I also encourage you to read all of!)