Frontend monads with consistent and beginner-friendly naming conventions.
Monads are function composers. They let you compose functions together to control your program flow.
Monads are a specific combination of ideas that you probably already understand. Because of this it can be hard to understand the whole but easy to grasp the parts.
Some almost monad things to help you understand:
All monads have these three methods. Some languages have different names for them, but their idea remains the same.
Unit is the monad constructor. It takes a value and returns a new monad of that state. Because monads deal with program flow and most monads have sort of sub types that represent one or more the states. The Unit's purpose is to let you declare a single monad of a specific state. E.g.
Some/None Left/Right Fetching/Error/Success
function unitExample() {
return test ? Some(value) : None();
}
The above function is consistent because it always returns a monad. However if test
is true it will return aSome
monad containing a value, otherwise it will return a None
.
9/10 times you'd use the specific unit constructor for the monad you want but each monad does have a .unit
method
The second core function of a monad is Flatmap. It's not however the flatter version of map, in fact flatMap provideds the basis for map. Flatmap says I'll pass the current value to your function, you return me another monad.
Other terms: bind, chain
Fronads chooses flatMap/map because they pair nicely.
Map builds off both unit and Flatmap, and is essentially value => flatMap(Unit(value))
.
Map is a convenient flatMap. It knows that you probably want the same monad again and so automatically creates the one you want. Letting you do things like:
Some(person).map(person => person.age)
The above statement will return a new some containing the persons age.
The Either monad describes situations that have two distinct states: Right and Left. Often used If the Either is Right map/Flatmap will be called and if the Either is Left leftMap/leftFlatMap will be called.
If Identity has 1 state, Maybe has 1.5, Either has 2.
Provide functions to map both sides of the Either
function biMap(leftFn: function, rightFn: function): Either
Change the Either to a Right or Left based on the result of a predicate
function filter(predicate: function): Either
If the Either is a right change to a Some if the Either is a Left drop the value and return a None
function toMaybe(): Maybe
Create a Try Either. func
is immediately exceuted,
if an error is thrown the Either will be Left(error) otherwise
the value of func
is passed to a Right.
function Try(func: function): Either
Identity is the vanilla ice cream of monads. Plain and simple, it is a function composer with only one state. Identity says: "I will always map your function to my value".
// Composition
foo(bar(value));
// Compose
Compose(foo, bar)(value);
// Identity
Identity(value)
.map(bar)
.map(value)
.value();
import {Identity} from 'fronads';
const add = aa => bb => aa + bb;
Identity(5)
.map(add(2))
.map(add(-10))
.value();
// -3
// Hocking a React component.
Some(UserProfile)
.map(connect(({user}) => ({user}))
.value();
Identity class
Perform a flatMap on the current identity
function flatMap(fn: function): Some
Return the identity's value. If the value is null, return defaultValue
function value(defaultValue?: *): *
The maybe monad is a way to represent null values without being forced to check for their existence. Maybe is the identity monad but with an added condition. The maybe says "I will only ever call map/flatmap if I am a Some. "
Some(5).map(ii => ii * 2) // Some(10)
None().map(ii => ii * 2) // None()
Maybe lets you declaratively write what should happen to data, but only excecutes if that data exists.
Some(value) None() Perhaps(value) PerhapsIn(value, path)
####
Maybe class
Return the maybe's value if it is 'some' or else return defaultValue
function value(defaultValue?: *): *
Change the Maybe to a Some or None based on the result of a predicate
function filter(predicate: function): Maybe
Change the Maybe to an Either. If Some
the value is placed in Right
if None
the value of leftValue
is placed in a Left
.
function toEither(leftValue: *): Either
Creates a new Maybe as 'Some' value
function Some(): Maybe
var person = Some({ name: "Derek Tibbs", child: Some({ name: "Derek Tibbs Jr" }) });
Creates a new Maybe as 'None' value
function None()
var person = Some({ name: "Derek Tibbs", child: None() });
Create a new Maybe where the value is uncertain.
function Perhaps(value: any): Maybe
var person = Perhaps(possibleNullValue);
Create a new Maybe from a deep uncertain value.
function PerhapsIn(value: any, path: Array.<string>): Maybe
var person = PerhapsIn({foo: {bar: possibleNullValue}}, ['foo', 'bar']);
Sometimes the state of your app can be represented through more than two states. The StateMonadFactory lets you create a monad with an arbitary number of state.
Given an array of names it will returns an on object of custom Unit functions.
StateFunctor(['Empty', 'Fetching', 'Refetching', 'Error', 'Success']) // { // EmptyState: EmptyState, // FetchingState: FetchingState, // RefetchingState: RefetchingState, // ErrorState: ErrorState, // SuccessState: SuccessState // } StateFunctor(['New', 'Edit', 'View']) // { // NewState: NewState, // EditState: EditState, // ViewState: ViewState // }
Task lets you create a description of an action that is resolved via a callback. It is useful for asynchronous operations. It can be thought of as a stricter more monadic version of a promise.
Task class
Perform a flatMap on the current value of the previous resolved computation
function flatMap(fn: TaskFlatMapper): Task
Perform a flatMap on the current value of the previous rejected computation
function leftFlatMap(fn: TaskFlatMapper): Task
Perform a map on the current value of the previous resolved computation
function map(fn: TaskMapper): Task
Perform a map on the current value of the previous rejected computation
function leftMap(fn: TaskMapper): Task
Run the computation and return the state as either a rejected or resolved promise.
function toPromise(): Promise
Create a new Task from a function that returns a promise.
function TaskPromise(value: any): Task