-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy pathmonad.js
114 lines (83 loc) · 3.23 KB
/
monad.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// monad.js
// Douglas Crockford
// 2012-10-17
// Public Domain
// The MONAD function is a macroid that produces monad constructor functions.
// It can take an optional modifier function, which is a function that is
// allowed to modify new monads at the end of the construction processes.
// A monad constructor (sometimes called 'unit' or 'return' in some mythologies)
// comes with three methods, lift, lift_value, and method, all of which can add
// methods and properties to the monad's prototype.
// A monad has a 'bind' method that takes a function that receives a value and
// is usually expected to return a monad.
// var identity = MONAD();
// var monad = identity("Hello world.");
// monad.bind(alert);
// var ajax = MONAD()
// .lift('alert', alert);
// var monad = ajax("Hello world.");
// monad.alert();
// var maybe = MONAD(function (monad, value) {
// if (value === null || value === undefined) {
// monad.is_null = true;
// monad.bind = function () {
// return monad;
// };
// }
// });
// var monad = maybe(null);
// monad.bind(alert);
function MONAD(modifier) {
'use strict';
// Each unit constructor has a monad prototype. The prototype will contain an
// is_monad property for classification, as well as all inherited methods.
var prototype = Object.create(null);
prototype.is_monad = true;
// Each call to MONAD will produce a new unit constructor function.
function unit(value) {
// Construct a new monad.
var monad = Object.create(prototype);
// In some mythologies 'bind' is called 'pipe' or '>>='.
// The bind method will deliver the unit's value parameter to a function.
monad.bind = function (func, args) {
// bind takes a function and an optional array of arguments. It calls that
// function passing the monad's value and bind's optional array of args.
// With ES6, this horrible return statement can be replaced with
// return func(value, ...args);
return func.apply(
undefined,
[value].concat(Array.prototype.slice.apply(args || []))
);
};
// If MONAD's modifier parameter is a function, then call it, passing the monad
// and the value.
if (typeof modifier === 'function') {
modifier(monad, value);
}
// Return the shiny new monad.
return monad;
}
unit.method = function (name, func) {
// Add a method to the prototype.
prototype[name] = func;
return unit;
};
unit.lift_value = function (name, func) {
// Add a method to the prototype that calls bind with the func. This can be
// used for ajax methods that return values other than monads.
prototype[name] = function () {
return this.bind(func, arguments);
};
return unit;
};
unit.lift = function (name, func) {
// Add a method to the prototye that calls bind with the func. If the value
// returned by the func is not a monad, then make a monad.
prototype[name] = function () {
var result = this.bind(func, arguments);
return result && result.is_monad === true ? result : unit(result);
};
return unit;
};
return unit;
}