MONADS

a single unit

The monadic curse is that once someone learns what monads are and how to use them, they lose the ability to explain them to other people.

We are NOT going to talk about that

The notion of Monads comes from mathematics, especially Category theory

So they will tell you that in order to understand monads, you must first learn Haskell. It's where you start, and you have to learn category theory; without those monads, they are meaningless. I think that's like saying in order to understand burritos you must first learn Spanish.

In programming, the monad was made popular by Haskell, and has been transposed into various languages, including JS. It is used as a way of containing values and controlling mutations.

fold :: Monoid m => t m -> m

But everything is OK Eyal, why do we need this new thing that we never had to use before?

Damn it, man, What problem does it aim to solve?

Example: Anytime you had to deal with a possible  'null', 'undefined' in javascript - The Maybe Monad could have helped. It's all about flow control

so,

You can think of a Monad as a container for a value or object. Arrays or Lists are a container for a collection of values, an Object is a collection of properties and sometime functions

They all have specific method that they implement ([].forEach, {}.keys(), etc)

About conventions

  • Begins with a capital letter
  • Has at least 3 specific functions (value, bind, map)

Before we continue...

Identity Monad

The name Identity is based on the term "identity" used for a function that simply returns the value given to it. Identity monad will effectively do the same.

const Identity = x => ({
    value: () => x,
    chain: f => f(x),
    map: f => Identity(f(x))
});

const one = Identity(1);
/*
What will be the reply:
  1. null
  2. []
  3. undefined
  4. throw an error
*/


function funky(o) {
  o = null;
}

var x = [];
funky(x);
console.info(x);

The beauty of this is that the arguments passed into it cannot ever be altered or changed: it is absolutely immutable without having to use any special APIs.

This brings us to this

A core principle in FP, and Monads.

const Identity = x => ({
    value: () => x,
    chain: f => f(x),
    map: f => Identity(f(x))
});

const one = Identity(1);

Method: value

Alternative names: emit, join, valueOf

const one = Identity(1);
console.log(one.value()); // 1

Method: chain

Alternative names: flatMap, bind

console.log(one.chain(a => a + 1)); //2
one.chain(a => SomeMonad(a + 1));

{Composition}

const Identity = x => ({
    value: () => x,
    chain: f => f(x),
    map: f => Identity(f(x))
});

const one = Identity(1);

Method: map

Alternative names: fMap ('functional map')

console.log(one.map(a => a + 1));
const Identity = x => ({
    emit: () => x,
    chain: f => f(x),
    map: f => Identity(f(x)),
    inspect: () => `Identity(${x})`
});

Method: inspect

  1. Higher-order function
  2. Once a value, whatever it may be, is in the container (the Monad), we'll need a way to run functions on it.
  3. Hey Eyal, it's just like the map function on an Array - except that now we have it on the Monad object - but yea, it works the same.
  4. When an object implements a map function,  we call it a Functor - something that runs functions
Identity.of(2).map(function(two){ return two + 2 })
//=> Identity(4)
Identity.of("flamethrowers").map(function(s){ return s.toUpperCase() })
//=> Identity("FLAMETHROWERS")
Identity.of("bombs").map(_.concat(' away')).map(_.prop('length'))
//=> Identity(10)

What does Map mean?

Identity.of = function(val) {
    return new Identity(val);
};

const one = Identity.of(1);

Up until now we just used the pure function form, however there is a constructor common form with the use .of()

When you see .of() in the code, it's usually an indication that this is a Monad (convention..remember?)

OK seriously, what is a monad?

There’s really nothing else to it. A monad is just a specification of the behavior of some functions. What those functions actually do is completely up to you.

List Monad

const List = x => ({
    emit: () => x,
    chain: f => f(x),
    map: f => List.of(f(x)),
    inspect: () => `List(${x})`
});

  • Assume .of() as constructor implemented
  • Constractor validates input as an Array

Array like Monad

//Add new method to Monad
concat: a => List.of(x.concat(a)),

//use as
const values = List.of([1, 3, 4, 7, 10]); 

values.concat([12]).inspect(); 
//[1, 3, 4, 7, 10, 12]
//Add new method to Monad
head: () => x[0],

//use as
const values = List.of([1, 3, 4, 7, 10]);

values.head() // 1

Maybe Monad

(Optional)

The most known and powerfull

Remember null and undefined?

Think of this as "maybe there is a value... but maybe there is not"

const Just = (x) => ({
  chain: f => f(x),
  emit: () => x,
  map: f => MaybeOf(f(x)),
  isJust: true,
  isNothing: false,
  inspect: () => `Just(${x})`,
});

const Nothing = (x) => ({
  chain: _ => Nothing(),
  emit: () => Nothing(),
  map: _ => Nothing(),
  isJust: false,
  isNothing: true,
  inspect: () => `Nothing`,
});

const MaybeOf = x => x === null || x === undefined || x.isNothing ? Nothing() : Just(x);

const exportMaybe = {
  of: MaybeOf
};

export { 
    exportMaybe as Maybe
}
const fahrenheitToCelsius = a => (a - 32) * 0.5556;

const reading1 = 15;
const temp2C = Maybe.of(reading2)
                    .map(fahrenheitToCelsius);

console.log(temp2C.inspect());
// > Nothing()
const reading2 = null;

Let's see an example

const temp1C = Maybe.of(reading1)
                    .map(fahrenheitToCelsius);

console.log(temp1C.inspect()); // > Just(-9.4444)
const MaybeOf = x =>
    x === null ||
    x === undefined ||
    x.isNothing ? Nothing() : Just(x);

It works and does not break, because of this

https://github.com/JasonStorey/Optional.js

// "login.js"

import Optional from 'optional-js';

// Define some simple operations
const getUserId = 
    username => username === 'root' ? 1234 : 0;

const verify = 
    userId => userId === 1234;

const login = 
    userId => console.log('Logging in as : ' + userId);
    
// Declare a potentially undefined value
const username = process.argv[2];

// Wrap username in an Optional, and build a pipeline using our operations
Optional.ofNullable(username)
        .map(getUserId)
        .filter(verify)
        .ifPresent(login);
class Maybe<T> {
    private constructor(private value: T | null) {}

    static of<T>(value: T) {
        if (!value) {
            throw Error("Provided value must not be empty");
        }
        return new Maybe(value);
    }

    static nothing<T>() {
        return new Maybe<T>(null);
    }

    static fromValue<T>(value: T) {
        return value ? Maybe.of(value) : Maybe.nothing<T>();
    }

    getOrElse(defaultValue: T) {
        return this.value === null ? defaultValue : this.value;
    }
}
https://codewithstyle.info/advanced-functional-programming-in-typescript-maybe-monad/

Examples

  • The result may or may not exist: solved by the Maybe monad.
  • A non-deterministic number of results: solved by the List monad.
  • Outside world interaction: solved by the IO monad.
  • Eventual result: solved by the Promise/Future monad.
  • Dependence on the state: solved by the State monad.
  • Errors: solved by the Error monad.
https://wiki.haskell.org/Monad#Interesting_monads

Read more

  • https://jrsinclair.com/articles/2016/marvellously-mysterious-javascript-maybe-monad/
    
  • https://dev.to/airtucha/functors-and-monads-in-plain-typescript-33o1
  • https://codewithstyle.info/advanced-functional-programming-in-typescript-maybe-monad/
  • https://github.com/MostlyAdequate/mostly-adequate-guide
  • https://github.com/monet/monet.js

 

Classroom Coding with Prof. Frisby on YouTube

Made with Slides.com