forked from robotroutine/scriptum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscriptum.js
11441 lines (7207 loc) · 320 KB
/
scriptum.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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
___ ___ ___ ___ ___ ___ ___
/\ \ /\ \ /\ \ ___ /\ \ /\ \ /\__\ /\__\
/::\ \ /::\ \ /::\ \ /\ \ /::\ \ \:\ \ /:/ / /::| |
/:/\ \ \ /:/\:\ \ /:/\:\ \ \:\ \ /:/\:\ \ \:\ \ /:/ / /:|:| |
_\:\~\ \ \ /:/ \:\ \ /::\~\:\ \ /::\__\ /::\~\:\ \ /::\ \ /:/ / ___ /:/|:|__|__
/\ \:\ \ \__\ /:/__/ \:\__\ /:/\:\ \:\__\ __/:/\/__/ /:/\:\ \:\__\ /:/\:\__\ /:/__/ /\__\ /:/ |::::\__\
\:\ \:\ \/__/ \:\ \ \/__/ \/_|::\/:/ / /\/:/ / /:/ \:\/:/ / /:/ \/__/ \:\ \ /:/ / \/__/~~/:/ /
\:\ \:\__\ \:\ \ |:|::/ / \::/__/ /:/ / \::/ / /:/ / \:\ /:/ / /:/ /
\:\/:/ / \:\ \ |:|\/__/ \:\__\ /:/ / \/__/ /:/ / \:\/:/ / /:/ /
\::/ / \:\__\ |:| | \/__/ \/__/ \/__/ \::/ / /:/ /
\/__/ \/__/ \|__| \/__/ \/__/
*/
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
██████████████████████████████████ CONSTANTS ██████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
const PREFIX = "$_"; // avoid property name collisions
export const NOOP = null; // no operation
export const NOT_FOUND = -1; // native search protocol
export const TAG = Symbol.toStringTag;
/*
█████ Order Protocol ██████████████████████████████████████████████████████████*/
// Javascript's order protocol but reference identity with a tagged object
export const LT = {
[TAG]: "Ordering",
run: -1,
valueOf: () => -1,
toString: () => "-1"
};
export const EQ = {
[TAG]: "Ordering",
run: 0,
valueOf: () => 0,
toString: () => "0"
};
export const GT = {
[TAG]: "Ordering",
run: 1,
valueOf: () => 1,
toString: () => "1"
};
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
████████████████████████████████████ STATE ████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/* Shared state between asynchronous computations. It forces `Serial` and other
async types to wrap the next continuation into a stack-safe `Promise`, that way
rendering them stack-save as well. Every instantiation of an async value
increases the counter by one. It is reset to zero as soon as it gets greater
that 100. There might be edge cases where the picked upper bound doesn't stop
a single or several parallel async computations from exhausting the stack. In
this case, the upper bound will be reduced, which would result in an increased
promise creation along the way. */
let asyncCounter = 0; // upper bound: 100
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
████████████████████████████ CROSS-CUTTING ASPECTS ████████████████████████████
███████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/*█████████████████████████████████████████████████████████████████████████████
████████████████████████████ ALGEBRAIC DATA TYPES █████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/*
█████ Product Type ████████████████████████████████████████████████████████████*/
export const product = type => (...ks) => o => {
for (const k of ks)
if (!(k in o)) throw new Err(`missing key "${k}"`);
return {
[TAG]: type,
get: o,
run: f => f(o)
};
};
export const product_ = type => (...ks) => (...vs) => {
const acc = {};
if (ks.length !== vs.length) throw new Err("key/value mismatch");
for (let i = 0; i < ks.length; i++)
acc[ks[i]] = vs[i];
return {
[TAG]: type,
get: acc,
run: f => f(acc)
};
};
/*
█████ Variant Types ███████████████████████████████████████████████████████████*/
/* Variant(/sum) and product types to create flexible and safe variants(/sums)
of products.
const Either = variant("Either", "Left", "Right") (cons0, cons);
const tx = Either.Right(5),
ty = Either.Left;
tx.run(Either.match({left: 0, right: x => x * x})); // yields 25
ty.run(Either.match({left: 0, right: x => x * x})); // yields 0
`Either` is the type constructor and `Either.Left`/`Either.Right` are value
constructors. `Either.match` is a helper to create typed objects that are case
exhaustive, i.e. supply all necessary cases of the given type.
A variant type expects a function per case as arguments. It then calls the right
function passing its internal value as an argument. This value (or values) are
hidden as free variables of a closure. This renders debugging harder. For this
reason each variant type includes a `get`-property you can access these internal
value(s) with. Please note that the usage of these values for programming
purposes may be unsafe depending on the specific variant type. Since a variant
value may include no, one or many values, `get` always yields an array. */
export const variant = (type, ...tags) => (...cons) => {
if (tags.length !== cons.length)
throw new Err("malformed variant type");
const o = tags.reduce((acc, tag, i) => {
acc[tag] = cons[i] (type, tag, tag[0].toLowerCase() + tag.slice(1));
return acc;
}, {});
o.match = O.matchPattern_(tags.map(tag =>
tag[0].toLowerCase() + tag.slice(1)));
return o;
};
// constant instead of function
export const cons0 = (type, tag, k) =>
({[TAG]: type, get: [], run: ({[k]: x}) => x, tag});
export const cons = (type, tag, k) => x =>
({[TAG]: type, get: [x], run: ({[k]: f}) => f(x), tag});
export const cons2 = (type, tag, k) => x => y =>
({[TAG]: type, get: [x, y], run: ({[k]: f}) => f(x) (y), tag});
export const cons3 = (type, tag, k) => x => y => z =>
({[TAG]: type, get: [x, y, z], run: ({[k]: f}) => f(x) (y) (z), tag});
// object as argument
export const consObj = (...ks) => (type, tag, k) => o => {
for (const k2 of ks)
if (!(k2 in o)) throw new Err(`missing key "${k2}"`);
return {
[TAG]: type,
get: o,
run: ({[k]: f}) => f(o),
tag
};
};
/*█████████████████████████████████████████████████████████████████████████████
█████████████████████████████████ APPLICATOR ██████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
// enables flat syntax by utilizing method chaining without relying on `this`
export const App = t => ({
app: x => App(t(x)), // applies the boxed fun
app_: y => App(x => t(x) (y)), // applies the 2nd arg of the boxed fun
map: f => App(f(t)), // applies the fun
map_: f => App(x => f(x) (t)), // applies the 2nd arg of the fun
get: t // gets the boxed value
});
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████████ ERRORS ████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
export const Err = TypeError; // shortcut
/*█████████████████████████████████████████████████████████████████████████████
█████████████████████████████████ EXCEPTIONS ██████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/* Indicates errors that are not immediately thrown, i.e. you can recover from
them without using `catch`. `Exception` is a subtype of `Error`. Accepts an
error message or an error object as argument. In the latter case, the original
stack trace is kept.. */
export class Exception extends Error {
constructor(s) {
if (s.message) {
super(s.message);
this.stack = this.stack + "\n\n" + s.stack;
}
else super(s);
this[TAG] = "Exception";
}
};
// accumulate exceptions
export class Exceptions extends Error {
constructor(...stack) {
const xs = [];
super("exceptions");
stack.forEach(e => {
if (e[TAG] === "Exceptions") xs.push.apply(xs, e.stack);
else xs.push(e);
});
this.stack = xs;
}
};
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████ LAZY EVALUATION ███████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/* Lazy evaluation has the following properties:
* evaluate only when needed
* evaluate only as far as necessary (to WHNF)
* evaluate at most once (sharing)
In this library lazy evaluation is realized with implicit thunks, i.e. thunks
like `() => expr` that behave like `expr` on the consuming side. The technique
is based on `Proxy`s with thunks as their targets. There are some limitations
to this Proxy-based thunk technique:
* `typeof` isn't intercepted by the proxy (always yields `object`)
* `===` doesn't trigger evaluation of proxy-based thunks
* `throw` doesn't trigger evaluation of proxy-based thunks
Especially the last case must be taken into account and is the reason why
combinator of certain types like `Option` enforce strict evaluation in the
context of equality checking. */
/*
█████ Constants ███████████████████████████████████████████████████████████████*/
const DETHUNK = PREFIX + "dethunk";
const EVAL = PREFIX + "eval";
const NULL = PREFIX + "null";
const THUNK = PREFIX + "thunk";
/*
█████ API █████████████████████████████████████████████████████████████████████*/
export const strict = x => {
if (x && x[THUNK]) return x[DETHUNK];
else return x;
};
export const lazy = thunk =>
new Proxy(thunk, new Thunk());
/*
█████ Implementation ██████████████████████████████████████████████████████████*/
class Thunk {
constructor() {
this.memo = NULL;
}
apply(f, that, args) {
// enforce evalutation to WHNF
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
return this.memo(...args);
}
get(f, k, p) {
// prevent evaluation in case of introspection
if (k === THUNK) return true;
else if (k === "constructor") return Thunk;
// enforce evaluation of a single layer
else if (k === EVAL) {
if (this.memo === NULL) {
this.memo = f();
return this.memo;
}
else if (this.memo && this.memo[THUNK] === true) {
this.memo = this.memo[EVAL];
return this.memo;
}
else return this.memo;
}
// enforce evaluation to WHNF
else if (k === DETHUNK) {
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
return this.memo;
}
// intercept implicit type casts
else if (k === Symbol.toPrimitive
|| k === "valueOf"
|| k === "toString") {
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
if (Object(this.memo) === this.memo) return this.memo[k];
else if (k === Symbol.toPrimitive) return hint =>
hint === "string" ? String(this.memo) : this.memo;
else if (k === "valueOf") return () => this.memo;
else return () => String(this.memo);
}
// enforce evaluation to WHNF due to array context
else if (k === Symbol.isConcatSpreadable) {
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
if (this.memo[Symbol.isConcatSpreadable]) return true;
else return false;
}
// enforce evaluation to WHNF due to property access
else {
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
// take method binding into account
if (Object(this.memo) === this.memo
&& this.memo[k]
&& this.memo[k].bind)
return this.memo[k].bind(this.memo);
else return this.memo[k];
}
}
getOwnPropertyDescriptor(f, k) {
// force evaluation to WHNF
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
return Reflect.getOwnPropertyDescriptor(this.memo, k);
}
has(f, k) {
// prevent evaluation in case of introspection
if (k === THUNK) return true;
// enforce evaluation to WHNF
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
return k in this.memo;
}
ownKeys(o) {
// enforce evaluation to WHNF
if (this.memo === NULL) {
this.memo = f();
while (this.memo && this.memo[THUNK] === true)
this.memo = this.memo[EVAL];
}
return Reflect.ownKeys(this.memo);
}
set(o) {
throw new Err("set op on immutable value");
}
}
/*█████████████████████████████████████████████████████████████████████████████
█████████████████████████████████ OVERLOADED ██████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
// Javascript built-in overloaded operators as functions
export const compare = x => y => x < y ? LT : x > y ? GT : EQ;
const compareOn_ = () => compBoth(compare);
export const max = x => y => x >= y ? x : y;
export const min = x => y => x <= y ? x : y;
/*█████████████████████████████████████████████████████████████████████████████
██████████████████████████████ PATTERN MATCHING ███████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/* Utilizes destructuring assignments to realize a limited form of pattern
matching. Destructuring assignments are malformed for the job:
* either they return `Undefined` if the outer layer of a shape doesn't exist
* or they throw an error if a nested layer of a shape doesn't exist
The `match` combinator adjusts native destructuring assignment so that thrown
errors are catched and undefined as an assignment is rejected. Each case passed
to `match` is a function that destructures its arguments and offers the full
flexibility of functons to further inspect the assigned values. Each argument
are passed redundantly in an array at the end of the parameter list for the
case function to have access to the original not destructed values:
match({baz: [5]}) (
_if(({bar: s}) => s)
.then(s => s.toUpperCase()),
_if(([x, y, z]) => [x, y, z])
.then(xs => xs.reverse()),
_if(({foo: s}) => s)
.then(s => s + "!"),
_if(({baz: [n]}) => Number(n) === n ? n : undefined)
.then(n => "*".repeat(n)),
_if(id) // default case
.then(_ => "otherwise"));
// matches the 4th case and yields "*****"
The above pattern matching comes without pre-runtime exhaustiveness check. */
export const match = (...args) => (...cases) => {
let r;
for (_case of cases) {
try {
r = _case(...args, args); // also pass the whole args
if (r === undefined) continue;
else break;
} catch(e) {continue}
}
if (r) return r()
else throw new Err("non-exhaustive pattern match");
};
export const match_ = (...cases) => (...args) => {
let r;
for (_case of cases) {
try {
r = _case(...args, args); // also pass the whole args
if (r === undefined) continue;
else break;
} catch(e) {continue}
}
if (r) return r()
else throw new Err("non-exhaustive pattern match");
};
export const _if = _case => ({
then: f => (...args) => {
const r = _case(...args);
switch (introspect(r)) {
case "Array": {
for (let x of r)
if (x === undefined) return x;
return () => f(r);
}
case "Object": {
for (let x of O.values(r))
if (x === undefined) return x;
return () => f(r);
}
case "Undefined": return r;
default: return () => f(r);
}
}
});
/*█████████████████████████████████████████████████████████████████████████████
████████████████████████████████ STACK SAFETY █████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/*
█████ Tail Recursion ██████████████████████████████████████████████████████████*/
/* Stack-safe tail-recursion and mutual tail-recursion using a trampoline. The
`next` and `done` constructors are used to encode recursive and the base cases
respectively. In addition, the `call` constructor can be used to defer function
calls. */
export const Loop = f => x => {
let o = f(x);
while (o.constructor !== Loop.base) {
switch (o.constructor) {
case Loop.call: {
o = o.f(o.x);
break;
}
case Loop.rec: {
o = f(o.x);
break;
}
case Thunk: {
o = strict(o);
break;
}
default: throw new Err("invalid constructor");
}
}
return o.x;
};
export const Loop2 = f => (x, y) => {
let o = f(x, y);
while (o.constructor !== Loop2.base) {
switch (o.constructor) {
case Loop2.call: {
o = o.f(o.x, o.y);
break;
}
case Loop2.rec: {
o = f(o.x, o.y);
break;
}
case Thunk: {
o = strict(o);
break;
}
default: throw new Err("invalid tag");
}
}
return o.x;
};
export const Loop3 = f => (x, y, z) => {
let o = f(x, y, z);
while (o.constructor !== Loop3.base) {
switch (o.constructor) {
case Loop3.call: {
o = o.f(o.x, o.y, o.z);
break;
}
case Loop3.rec: {
o = f(o.x, o.y, o.z);
break;
}
case Thunk: {
o = strict(o);
break;
}
default: throw new Err("invalid tag");
}
}
return o.x;
};
// constructors
Loop.call = (f, x) => ({constructor: Loop.call, f, x});
Loop.rec = x => ({constructor: Loop.rec, x});
Loop.base = x => ({constructor: Loop.base, x});
Loop2.call = (f, x, y) => ({constructor: Loop2.call, f, x, y});
Loop2.rec = (x, y) => ({constructor: Loop2.rec, x, y});
Loop2.base = x => ({constructor: Loop2.base, x});
Loop3.call = (f, x, y, z) => ({constructor: Loop3.call, f, x, y, z});
Loop3.rec = (x, y, z) => ({constructor: Loop3.rec, x, y, z});
Loop3.base = x => ({constructor: Loop3.base, x});
/*
█████ Tail Recurson Modulo Cons & Beyond ██████████████████████████████████████*/
/* Stack-based trampoline to encode recursive cases not in tail call position.
It can mimick tail recursion modulo cons and more complex operations not in
tail position.
The original Fibbonacci algorithm
const fib_ = n =>
n <= 1 ? n
: fib_(n - 1) + fib_(n - 2);
is transformed into a trampolining version:
const add = x => y => x + y;
const fib = Loops(n =>
n <= 1
? Loops.base(n)
: Loops.call2(
add,
Loops.rec(n - 1),
Loops.rec(n - 2))); */
export const Loops = f => x => {
const stack = [f(x)];
while (stack.length > 1 || stack[0].constructor !== Loops.base) {
let o = stack[stack.length - 1];
switch (o.constructor) {
case Loops.call:
case Loops.call2: {
o = f(o.x.x); // 1st x of call and 2nd x of next tag
stack.push(o);
break;
}
case Loops.rec: {
o = f(o.x);
break;
}
case Loops.base: {
while (stack.length > 1 && stack[stack.length - 1].constructor === Loops.base) {
const p = (stack.pop(), stack.pop());
switch (p.constructor) {
case Loops.call: {
o = Loops.base(p.f(o.x));
stack.push(o);
break;
}
case Loops.call2: {
o = Loops.call(p.f(o.x), p.y);
stack.push(o);
break;
}
default: throw new Err("invalid constructor");
}
}
break;
}
case Thunk: {
o = strict(o);
break;
}
default: throw new Err("invalid constructor");
}
}
return stack[0].x;
};
export const Loops2 = f => (x, y) => {
const stack = [f(x, y)];
while (stack.length > 1 || stack[0].constructor !== Loops2.base) {
let o = stack[stack.length - 1];
switch (o.constructor) {
case Loops2.call:
case Loops2.call2: {
o = f(o.x.x, o.x.y);
stack.push(o);
break;
}
case Loops2.rec: {
o = f(o.x, o.y);
break;
}
case Loops2.base: {
while (stack.length > 1 && stack[stack.length - 1].constructor === Loops2.base) {
const p = (stack.pop(), stack.pop());
switch (p.constructor) {
case Loops2.call: {
o = Loops2.base(p.f(o.x, o.y));
stack.push(o);
break;
}
case Loops2.call2: {
o = Loops2.call(p.f(o.x, o.y), p.y);
stack.push(o);
break;
}
default: throw new Err("invalid constructor");
}
}
break;
}
case Thunk: {
o = strict(o);
break;
}
default: throw new Err("invalid constructor");
}
}
return stack[0].x;
};
// constructors
Loops.call = (f, x) => ({constructor: Loops.call, f, x});
Loops.call2 = (f, x, y) => ({constructor: Loops.call2, f, x, y});
Loops.rec = x => ({constructor: Loops.rec, x});
Loops.base = x => ({constructor: Loops.base, x});
Loops2.call = (f, x) => ({constructor: Loops2.call, f, x});
Loops2.call2 = (f, x, y) => ({constructor: Loops2.call2, f, x, y});
Loops2.rec = x => y => ({constructor: Loops2.rec, x, y});
Loops2.base = x => ({constructor: Loops2.base, x});
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
███████████████████████████ TYPE CLASS COMBINATORS ████████████████████████████
███████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/*█████████████████████████████████████████████████████████████████████████████
██████████████████████████████████ FOLDABLE ███████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
export const foldAll = Foldable => p => Foldable.foldr(x => acc =>
p(x) ? acc : false) (true);
export const foldAnd = Foldable => Foldable.foldr(b => acc => b && acc) (true);
export const foldAny = Foldable => p => Foldable.foldr(x => acc =>
p(x) ? true : acc) (false);
export const foldMapl = (Foldable, Monoid) => f =>
Foldable.foldl(compSnd(Monoid.append) (f)) (Monoid.empty);
export const foldMapr = (Foldable, Monoid) => f =>
Foldable.foldr(comp(Monoid.append) (f)) (Monoid.empty);
export const foldMax = (Foldable, Order) => tx => Foldable.foldl1(Order.max) (tx);
export const foldMin = (Foldable, Order) => tx => Foldable.foldl1(Order.min) (tx);
export const foldOr = Foldable => Foldable.foldr(b => acc => b || acc) (false);
/*█████████████████████████████████████████████████████████████████████████████
███████████████████████████████████ FUNCTOR ███████████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
export const mapEff = Functor => x => Functor.map(_ => x);
/*█████████████████████████████████████████████████████████████████████████████
██████████████████████████████ FUNCTOR :: APPLY ███████████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
export const apEff1 = Apply => tx => ty => Apply.ap(Apply.map(_const) (tx)) (ty);
export const apEff2 = Apply => tx => ty => Apply.ap(mapEff(Apply) (id) (tx)) (ty);
export const liftA2 = Apply => f => tx => ty => Apply.ap(Apply.map(f) (tx)) (ty);
/*█████████████████████████████████████████████████████████████████████████████
██████████████████████████ FUNCTOR :: APPLY :: CHAIN ██████████████████████████
███████████████████████████████████████████████████████████████████████████████*/
/* It is possible to chain several monads of the same type in a principled
fashion while maintaining a flat composition syntax, provided all subsequent
invocations of the partially applied Kleisli action are wrapped in a minimal
functiorial context.
This means the technique leaks into the call side and adds some syntactical
noise but it is the best we can hope for. Please note that the following isn't
possible with the classic `liftM2` combinator, which is merely applicative
effect combination in disguise.
const chain2_ = chain2(Monad);
^^^^^
type class
chain2_([1,2,3]) ([4,5,6]) (x => x & 1 ? of(y => [x + y]) : []);
^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^
monad monad next computation depends on x */
export const chain2 = Chain => mx => my => fm =>
Chain.chain(mx) (x => Chain.chain(fm(x)) (gm => Chain.chain(my) (gm)));
export const chain3 = Chain => mx => my => mz => fm =>
Chain.chain(mx) (x =>
Chain.chain(fm(x)) (gm =>
Chain.chain(my) (y =>