Fronads

fronads npm

Frontend monads with consistent and beginner-friendly naming conventions.

docs

What is a monad?

Monads are function composers. They let you compose functions together to control your program flow.

Almost monads

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

All monads have these three methods. Some languages have different names for them, but their idea remains the same.

Unit

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

flatMap

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

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.

Modules

Either

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.

class Either

Either()

function Either(value: any, isRight: boolean)

either.unit()

Unit function

function unit(value: any): Either

either.flatMap()

flatMap

function flatMap(fn: function): Either

either.map()

map

function map(fn: function): Either

either.leftFlatMap()

leftFlatMap

function leftFlatMap(fn: function): Either

either.leftMap()

leftMap

function leftMap(fn: function): Either

either.ap()

ap

function ap(eitherWithFn: Either): Either

either.biMap()

Provide functions to map both sides of the Either

function biMap(leftFn: function, rightFn: function): Either

either.biFlatMap()

biFlatMap

function biFlatMap(leftFn: function, rightFn: function): Either

either.value()

Return the current value.

function value(): any

either.filter()

Change the Either to a Right or Left based on the result of a predicate

function filter(predicate: function): Either

either.toLeft()

Force the Either to a Left

function toLeft(): Left

either.toRight()

Force the Either to a Right

function toRight(): Right

either.toMaybe()

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

Right()

Create an Either as a Right value

function Right(value: any): Either

Left()

Create an Either as a Left value

function Left(value: any): Either

Try()

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

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();

Examples

  • Hocks
  • repeated Composition.
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();

class Identity

Identity class

new Identity()

Identity constructor

new Identity(value: *)

identity.unit()

Unit function

function unit(value: any): Identity

identity.flatMap()

Perform a flatMap on the current identity

function flatMap(fn: function): Some

identity.map()

map

function map(fn: function): Identity

identity.value()

Return the identity's value. If the value is null, return defaultValue

function value(defaultValue?: *): *

identity.toEither()

Change the Identity to a Right Either.

function toEither(): Either

identity.toMaybe()

Change the Identity to a Some Maybe.

function toMaybe(): Maybe

Maybe

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.

Units

Some(value) None() Perhaps(value) PerhapsIn(value, path)

Examples

Unknown Deep Children

Don't Render without data

####

class Maybe

Maybe class

new Maybe()

Maybe constructor

new Maybe(value: *, isSome: boolean)

maybe.unit()

Unit function

function unit(value: any): Either

maybe.flatMap()

Perform a flatMap on the current maybe

function flatMap(fn: function): Some

maybe.map()

map

function map(fn: function): Maybe

maybe.value()

Return the maybe's value if it is 'some' or else return defaultValue

function value(defaultValue?: *): *

maybe.filter()

Change the Maybe to a Some or None based on the result of a predicate

function filter(predicate: function): Maybe

maybe.toEither()

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

Some()

Creates a new Maybe as 'Some' value

function Some(): Maybe

Example
var person = Some({
    name: "Derek Tibbs",
    child: Some({
        name: "Derek Tibbs Jr"
    })
});

None()

Creates a new Maybe as 'None' value

function None()

Example
var person = Some({
    name: "Derek Tibbs",
    child: None()
});

Perhaps()

Create a new Maybe where the value is uncertain.

function Perhaps(value: any): Maybe

Example
var person = Perhaps(possibleNullValue);

PerhapsIn()

Create a new Maybe from a deep uncertain value.

function PerhapsIn(value: any, path: Array.<string>): Maybe

Example
var person = PerhapsIn({foo: {bar: possibleNullValue}}, ['foo', 'bar']);

State

StateFunctor

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.

Example
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
// }

class StateFunctor

stateFunctor.StateFunctor()

State constructor

function StateFunctor(value: *, stateBooleans: object): State

Task

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.

class Task

Task class

new Task()

Task constructor

new Task(fn: TaskComputation)

task.unit()

Unit function

function unit(fn: TaskComputation): Task

task.flatMap()

Perform a flatMap on the current value of the previous resolved computation

function flatMap(fn: TaskFlatMapper): Task

task.leftFlatMap()

Perform a flatMap on the current value of the previous rejected computation

function leftFlatMap(fn: TaskFlatMapper): Task

task.map()

Perform a map on the current value of the previous resolved computation

function map(fn: TaskMapper): Task

task.leftMap()

Perform a map on the current value of the previous rejected computation

function leftMap(fn: TaskMapper): Task

task.run()

Run the the series of compututations.

function run()

task.toPromise()

Run the computation and return the state as either a rejected or resolved promise.

function toPromise(): Promise

TaskFactory()

Create a new Task

function TaskFactory(computation: TaskComputation): Task

Resolve()

Create a new resolved Task

function Resolve(value: any): Task

Reject()

Create a new rejected Task

function Reject(value: any): Task

TaskPromise()

Create a new Task from a function that returns a promise.

function TaskPromise(value: any): Task