TsMonad
- a simple and pragmatic monad library
- designed for TypeScript
- with the aim of limiting errors due to unhandled nulls
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.
Node:
var TsMonad = require('tsmonad');
Browser:
<script src="node_modules/tsmonad/dist/tsmonad.js"></script>
TypeScript definitions:
/// <reference path="node_modules/tsmonad/dist/tsmonad.d.ts" />
Examples (in TypeScript)
You can see the unit tests for the examples below online here and view the source in test/examples.ts.
Pattern matching emulation
.caseOf; .caseOf; .caseOf;
General Maybe usage
The Maybe monad can simplify processing of values that may not exist:
.bindgetBusPassage // not all ages have a bus pass, this is a Maybe<BusPass> .caseOf;
Without Maybe, this would be something like:
age = user.getAge; // might be null or undefined if age 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.defaultingfalse; // Maybe<false>user.getLikesCookies.valueOrfalse; // falseuser.getLikesCookies.valueOrComputeexpensiveCalculation;user.getLikesCookies.valueOrThrownew Error; // Maybe.just({ three: 3, hi: 'hi'})Maybe.sequence; // Maybe.nothingMaybe.sequence;
General Either usage
.bindgetBusPassage // either busPass or 'Too young for a bus pass' - type of Either<string,BusPass> .caseOf;
General Writer usage
Somewhat contrived example of recording arithmetic operations:
.bindWriter.writer, x + 8 .bindWriter.writer, 8 * x - 6 .caseOf);
The lift method (fmap)
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.
.liftn * 2 .caseOf;
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.
FAQ and apologies
Why only Maybe, Either and Writer (so far)?
These monads are the most obviously useful in JavaScript's world of unrestricted mutable state and side effects. I'm currently evaluating which other common monads offer enough benefit to be worth implementing in TypeScript.
Where's monad transformers?
Sorry. One day. But for the moment it's not practicable to do this without support for higher-kinded types.
Fantasy Land conformant?
Is itYes - there are aliases for Fantasy Land interfaces of Functor and Monad.