-
Notifications
You must be signed in to change notification settings - Fork 3
/
par.js
104 lines (93 loc) · 2.43 KB
/
par.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
const daggy = require('daggy')
const { of, ap } = require('sanctuary-type-classes')
const patch = require('./fl-patch')
const { id, compose } = require('./utils')
// data Par f a where
// Pure :: a -> Par f a
// Lift :: f a -> Par f a
// Ap :: Par f (a -> b) -> Par f a -> Par f b
const Par = daggy.taggedSum('Par', {
Pure: ['x'],
Lift: ['i'],
Ap: ['f', 'x'],
})
const { Pure, Lift, Ap } = Par
Object.assign(Par, patch({
// :: a -> Par f a
of: Pure,
// :: f a -> Par f a
lift: Lift,
}))
Object.assign(Par.prototype, patch({
// :: Par f a ~> (a -> b) -> Par f b
map(f) {
return ap(Par.of(f), this)
},
// :: Par f a ~> Par f (a -> b) -> Par f b
ap(that) {
return Ap(that, this)
},
// :: (Applicative g) => Par f a ~> (Ɐ x. f x -> g x, TypeRep g) -> g a
foldPar(f, T) {
const argsF = [this]
const fns = []
while (true) {
let argF = argsF.pop()
if (Ap.is(argF)) {
const lengthInitial = argsF.length
while (Ap.is(argF)) {
argsF.push(argF.x)
argF = argF.f
}
fns.push(Fn(foldArg(argF, f, T), argsF.length - lengthInitial))
continue
}
const argT = foldArg(argF, f, T)
if (fns.length === 0) {
return argT
}
let fn = fns.pop()
let res = ap(fn.f, argT)
if (fn.length > 1) {
fns.push(Fn(res, fn.length - 1))
continue
}
while (fns.length > 0) {
fn = fns.pop()
res = ap(fn.f, res)
if (fn.length > 1) {
fns.push(Fn(res, fn.length - 1))
break
}
}
if (fns.length === 0) {
return res
}
}
},
// :: Par f a ~> (Ɐ x. f x -> g x) -> Par g a
hoistPar(f) {
return this.foldPar(compose(Par.lift)(f), Par)
},
// :: (Applicative f) => Par f a ~> TypeRep f -> f a
retractPar(m) {
return this.foldPar(id, m)
},
// :: Par f a ~> (Ɐ x. f x -> Par g x) -> Par g a
graftPar(f) {
return this.foldPar(f, Par)
},
}))
// Internal helper function for foldPar it folds only Pure and Lift nodes
const foldArg = (node, f, T) => {
if (Pure.is(node)) {
return of(T, node.x)
} else if (Lift.is(node)) {
return f(node.i)
}
}
// Internal helper structure for foldPar it conatins an Applicative containing
// a function and information on how many argument it needs
// type Fn g a b = { fun:: g (a -> b), length:: Number}
const Fn = (f, length) => ({ f, length })
module.exports = Par