-
Notifications
You must be signed in to change notification settings - Fork 3
/
examples.js
219 lines (190 loc) · 5.98 KB
/
examples.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
require('jscategory');
// Algebraic theory of MONOIDS:
// Choose a set and two functions satisfying some relations, get a monoid
// It's undecidable to test the relations, so we leave them out...
var monoid = function (set, times, ident) {
return prods({
t: func,
"*": hom(set, set, set),
1: set
})({
t: set,
"*": times,
1: ident
});
};
// ... and write tests instead.
// Given a monoid and a way to get random elements of the monoid,
// check associativity and unit laws.
var testMonoid = function (mon, rand) {
var a = rand(), b = rand(), c = rand();
var i = mon[1];
var t = mon["*"];
if (t(a, t(b, c)) !== t(t(a, b), c)) {
throw new Error("Monoid is not associative: " + stringify([a, b, c]));
}
if (t(a, i) !== a) {
throw new Error("Monoid does not satisfy a * 1 = a: " + a);
}
if (t(i, a) !== a) {
throw new Error("Monoid does not satisfy 1 * a = a: " + a);
}
};
// Alternate definition of a monoid that helps illustrate
// the concept of a monoid homomorphism
var monoid2 = function (setIn, setOut, times, ident) {
// Allow the other signature as well
if (arguments.length === 3) {
ident = times;
times = setOut;
setOut = setIn;
}
return prods({
"*": hom(setIn, setIn, setOut),
1: hom(/*terminal,*/ setOut)
})({
"*": times,
1: ident
});
};
var K = function (x) { return function () { return x; }; };
var and = hom(boolean, boolean, boolean)(function (b1, b2) { return b1 && b2; });
var andMon = monoid2(boolean, and, K(true));
var xor = hom(boolean, boolean, boolean)(function (b1, b2) { return (b1 ^ b2) ? true : false; });
var xorMon = monoid2(boolean, xor, K(true));
var int32Add = hom(int32, int32, int32)(function (n1, n2) { return n1 + n2; });
var int32AddMon = monoid2(int32, int32Add, K(0));
var concat = hom(string, string, string)(function (s1, s2) { return s1 + s2; });
var concat = monoid2(string, concat, K(""));
var mod2 = hom(int32, boolean)(function (n) { return (n % 2) ? true : false; });
var lsb1 = monoid2(mod2, boolean, xor, K(true));
var lsb2 = monoid2(int32, mod2, int32Add, K(0));
// TODO: monoid hom as pullback of the two ways of computing lsb
// MONADS as monoid objects in an endofunctor category
var monad = function (ftor, times, ident) { // var monoid = function (set, times, ident) {
return function (t) {
func(t);
return prods({ // return prods({
t: func, // t: func,
"*": hom(ftor(ftor(t)), ftor(t)), // "*": hom(set, set, set),
1: hom(t, ftor(t)) // 1: hom(set)
})({ // })({
t: ftor(t), // t: set,
"*": times(t), // "*": times,
1: ident(t) // 1: ident
}); // });
}; // };
}
var lift = function (t) {
func(t);
return hom(t, arrayOf(t))(function (x) { return [x]; });
};
var flatten = function (t) {
func(t);
return hom(arrayOf(arrayOf(t)), arrayOf(t))(function (llx) {
var result = [];
var len = llx.length;
for (var i = 0; i < len; ++i) {
result = result.concat(llx[i]);
}
return result;
});
};
var listMon = monad(arrayOf, flatten, lift);
var loi = listMon(int32);
try {
loi.t([3,6,5]); // passes
loi.t(["3",6,5]); // fails on item 0
} catch (e) {}
loi[1](3); // === [3]
loi["*"]([[1,2],[3,4],[5]]); // === [1,2,3,4,5]
var upto = hom(int32, arrayOf(int32))(function (x) {
var r = [];
for (var i = 0; i < x; ++i) {
r.push(i);
}
return r;
});
loi["*"](listMon(upto).t([6,2])); // === [0,1,2,3,4,5,0,1]
// MONADS in the Haskell / Kleisli style
var kleisli = function (mon) {
return function (t) {
var mont = mon(t);
function M(mx) {
return {
value: mx,
_: function (f) {
return M(mont["*"](mon(f).t(this.value)));
}
};
}
return function (x) { return M(mont[1](x)); };
};
};
var kli = kleisli(listMon)(int32);
kli(6)._(upto)._(upto).value; // === [0,0,1,0,1,2,0,1,2,3,0,1,2,3,4]
// Example of a monoidal closed functor, the "points" functor.
// Comes equipped with natural isomorphisms
// chi:(lazy A -o B) -> (lazy A -o lazy B)
// phi:(lazy prod_i A_i) -> (prod_i lazy A_i)
// http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf
// has lots of examples showing how laziness helps modularization
var lazy = function (c) {
// Restrict hom to a single argument,
// i.e. [] -> c
return hom(c);
};
var lazyLift = K;
var lazyFlatten = function (lazyLazyX) {
return lazyLazyX();
};
var lazyMon = monad(lazy, lazyLift, lazyFlatten);
var lazyChi = function (lazyAtoB) {
return function (lazyA) {
return lazyLift(lazyAtoB()(lazyA()));
};
};
var lazyPhi = function (lazyProd) {
// Invoke the lazy prod to get the actual prod,
// then delay each element.
return array(lazyProd()).mapClean(lazyLift);
};
// Another monoidal closed functor,
// continuation passing is double negation.
// Dinatural in Z:
// cp A = (A->Z)->Z
var cp = function (c) {
return hom(hom(c, id), id);
};
var cpLift = function (x) {
return function (k) {
return k(x);
};
};
// Quadruple negation to double negation
// (((X->Z)->Z)->Z)->Z -> (X->Z)->Z
var cpFlatten = function (cpCpX /*: (((X->Z)->Z)->Z)->Z */) {
return function (k /*: (X->Z) */) {
return cpCpX(function (l) {
return l(k); /*: Z */
});
};
};
var cpMon = monad(cp, cpLift, cpFlatten);
var cpChi = function (k /*: ((A->B)->Z)->Z */) {
return function (l /*: (A->Z)->Z */) {
return function (m /*: B->Z */) {
return l(function (c /*: A */) {
return k(function (d /*: A->B */) {
return m(d(c));
});
});
};
};
};
// ((prod_i A_i)->Z)->Z -> prod_i (((A_i)->Z)->Z)
var cpPhi = function (cpProd) {
var prod;
cpProd(function (p) { prod = array(p); });
return p.mapClean(cpLift);
};