0.8.8 • Published 5 years ago

tsmonad42 v0.8.8

Weekly downloads
2
License
MIT
Repository
github
Last release
5 years ago

tsmonad42

  • a simple and pragmatic monad library
  • designed for TypeScript
  • with the aim of limiting errors due to unhandled nulls
  • forked from TsMonad (https://github.com/cbowdon/TsMonad.git) written by Chris Bowdon

Description

This library provides implementations of the most useful monads outside of Haskell (subjectively, this is Maybe and Either). It also provides a strongly-typed emulation of pattern matching to help enforce program correctness.

I won't presume to attempt a monad tutorial here. There are several online - I recommend Douglas Crockford's Monads & Gonads talk.

License

MIT

Usage

This library will work with vanilla ES3 JavaScript with node or in the browser. However, it is far better with TypeScript.

NodeJS:

var TsMonad = require('tsmonad');

or

import { Either } from 'tsmonad42';

Browser:

<script src="node_modules/tsmonad/dist/tsmonad.js"></script>

TypeScript definitions:

/// <reference path="node_modules/tsmonad/dist/tsmonad.d.ts" />

Pattern matching emulation

var turns_out_to_be_100 = Maybe.just(10)
    .caseOf({
        just: n => n * n,
        nothing: () => -1
    });

var turns_out_to_be_a_piano = Maybe.nothing<number>()
    .caseOf({
        just: n => n * n,
        nothing: () => -1 // joke, it's negative one not a piano
    });

var turns_out_to_throw_a_compiler_error = Maybe.just(321)
    .caseOf({
        just: n => 999,
        // tsc will tell you that this "does not implement 'nothing'"
        // helping to enforce correct handling of all possible paths
    });

General Maybe usage

The Maybe monad can simplify processing of values that may not exist:

var canRideForFree = user.getAge()  // user might not have provided age, this is a Maybe<number>
    .bind(age => getBusPass(age))   // not all ages have a bus pass, this is a Maybe<BusPass>
    .caseOf({
        just: busPass => busPass.isValidForRoute('Weston'),
        nothing: () => false
    });

Without Maybe, this would be something like:

var canRideForFree,
    age = user.getAge(); // might be null or undefined

if (age) {
    var busPass = getBusPass(age); // might be null or undefined
    if (busPass) {
        canRideForFree = busPass.isValidForRoute('Weston');
    }
}
canRideForFree = false;

Please excuse the messy var scoping and implicit any types in the above. Again, the neat thing about the caseOf method is that it forces you to consider the failure case - it's not always obvious if you're missing a branch of your if-else statement, until it blows up at runtime.

There are some convenience methods in Maybe:

user.getLikesCookies().defaulting(false); // Maybe<false>
user.getLikesCookies().valueOr(false); // false
user.getLikesCookies().valueOrCompute(() => expensiveCalculation());
user.getLikesCookies().valueOrThrow(new Error());

// Maybe.just({ three: 3, hi: 'hi'})
Maybe.sequence<number|string>({ three: Maybe.just(3), hi: Maybe.just('hi') });

// Maybe.nothing
Maybe.sequence<number>({ three: Maybe.just(3), hi: Maybe.nothing() });

General Either usage

var canRideForFree = user.getAge()  // either 42 or 'Information withheld' - type of Either<string,number>
    .bind(age => getBusPass(age))   // either busPass or 'Too young for a bus pass' - type of Either<string,BusPass>
    .caseOf({
        right: busPass => busPass.isValidForRoute('Weston'),
        left: errorMessage => { console.log(errorMessage); return false; }
    });

General Writer usage

Somewhat contrived example of recording arithmetic operations:

var is_true = Writer.writer(['Started with 0'], 0)
    .bind(x => Writer.writer(['+ 8'], x + 8))
    .bind(x => Writer.writer(['- 6', '* 8'], 8 * (x - 6)))
    .caseOf({
        writer: (s, v) => v === 16 && s.join(', ') === 'Started with 0, + 8, - 6, * 8'
    }));

The lift method

The lift method takes a lambda, applies it to the wrapped value and calls the unit function of the monad on the result (e.g. for Maybe it calls just). Useful when you want to bind to a function that doesn't return a monad.

var turns_out_to_be_true = Maybe.just(123)
    .lift(n => n * 2)
    .caseOf({
        just: n => n === 246,
        nothing: () => false
    });

Note that for Maybe, if the lifted function returns null or undefined then it returns Nothing rather than wrapping a null in a Just, which is perverse.

0.8.8

5 years ago

0.8.7

5 years ago

0.8.6

5 years ago

0.8.5

5 years ago

0.8.4

5 years ago

0.8.3

5 years ago

0.8.2

5 years ago

0.8.1

5 years ago

0.8.0

5 years ago