green_curry
Version:
Curried functional programming library
263 lines (207 loc) • 5.5 kB
JavaScript
var hack = arr => function () {
arr.push (this)
return 0
}
const F = module.exports = {
/////////////
// //
// Basic //
// //
/////////////
// 'a -> 'a
id: x => x,
// 'a -> unit -> 'a
const: x => () => x,
// 'a -> unit
ignore: () => undefined,
// (unit -> 'a) -> 'a
exec: f => f (),
// bool -> unit
throw: x => {throw x},
// 'a -> unit
log: console.log.bind (console),
// 'a -> string array -> int -> unit
log_json: x => r => i => console.log (JSON.stringify (x, r, i)),
// string -> 'a
// wrapper for eval required
// operates at global scope (ie: ignores variables) if pointless form
eval: x => eval (x),
'=': x => y => {
if (x === y) {
return true
}
else if (typeof x === 'object' && typeof y === 'object') {
if (Array.isArray (x) !== Array.isArray (y)) {
return false
}
else if (Array.isArray (x) && Array.isArray (y)) {
if (x.length !== y.length) {
return false
}
for (let i = 0; i < x.length; i++) {
if (! F['='] (x[i]) (y[i])) {
return false
}
}
return true
}
else {
const kx = Object.keys (x)
const ky = Object.keys (y)
if (kx.length !== ky.length) {
return false
}
for (let i = 0; i < kx.length; i++) {
if (! F['='] (x[kx[i]]) (y[kx[i]])) {
return false
}
}
return true
}
}
else {
return false
}
},
'==': x => y => x == y,
'===': x => y => x === y,
'!=': x => y => x != y,
'<>': x => y => x != y,
'!==': x => y => x !== y,
'>': x => y => x > y,
'>=': x => y => x >= y,
'<': x => y => x < y,
'<=': x => y => x <= y,
'!': x => ! x,
'~': x => ~ x,
'+': x => y => x + y,
'-': x => y => x - y,
'*': x => y => x * y,
'/': x => y => x / y,
'%': x => y => x % y,
'|': x => y => x | y,
'||': x => y => x || y,
'&': x => y => x & y,
'&&': x => y => x && y,
'^': x => y => x ^ y,
'>>>': x => y => x >> y,
'>>>>': x => y => x >>> y,
'<<<': x => y => x << y,
'??': x => y => ! [undefined, null].includes (x) ? x : y,
'?:': x => y => z => x ? y : z,
'|>': x => f => f (x),
'@@': x => f => f (x),
'<|': f => x => f (x),
'>>': f => g => x => g (f (x)),
'<<': f => g => x => f (g (x)),
/////////////////
// //
// Functions //
// //
/////////////////
// ('a -> bool) -> ('a -> bool)
neg: f => x => ! f (x),
// ('a -> bool) -> ('a -> bool) -> ('a -> bool)
union: f => g => x => f (x) || g (x),
// ('a -> bool) -> ('a -> bool) -> ('a -> bool)
inter: f => g => x => f (x) && g (x),
try: f => {
return {
catch: g => {
try {
return f ()
}
catch (err) {
return g (err)
}
}
}
},
// (a' -> 'b -> 'c) -> 'b -> 'a -> 'c
swap: f => x => y => f (y) (x),
// int -> ('a -> 'b) -> unit
delay: t => f => setTimeout (f, t),
// ('a -> 'b) -> 'a -> 'a
tap: f => x => (f (x), x),
// note: all composition functions here are reverse composition
// if JavaScript had operator overloading, i'd happily
// use that and type it properly as
// ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
// but without an infix operator, it'd be verbose non-variadic
// (? -> ?) array -> (? -> ?)
rcomp: fs => x => fs.reduce ((a, h) => h (a), x),
// alternatively this operator overloading hack that i lifted from:
// http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/#slide-33
c: () => {
const fs = []
const valueOf = Function.prototype.valueOf
Function.prototype.valueOf = hack (fs)
return f => {
Function.prototype.valueOf = valueOf
return F.rcomp (fs.length ? fs : [f || F.id])
}
},
// adaptation of above that pipes a given argument through the composed function
p: x => {
const fs = []
const valueOf = Function.prototype.valueOf
Function.prototype.valueOf = hack (fs)
return f => {
Function.prototype.valueOf = valueOf
return F.rcomp (fs.length ? fs : [f || F.id]) (x)
}
},
// ('a -> 'b) -> ('a -> 'b)
memoize: f => {
const memo = []
return x => {
const len = memo.length
for (var i = 0; i < len; i++)
if (memo [i] [0] === x)
return memo [i] [1]
memo [len] = [x, f (x)]
return memo [len] [1]
}
},
// int -> (unit -> unit) -> unit
times: x => f => {for (var n = 0; n < x; n++) f ()},
// int -> ('a -> 'b') -> ('a -> unit/'b)
after: n => f => x => n-- >= 1 ? undefined : f (x),
// int -> ('a -> 'b') -> ('a -> unit/'b)
before: n => f => x => n-- >= 1 ? f (x) : undefined,
match: x => {
const cases = []
const o = {
case: p => f => {
cases.push ([p, f])
return o
},
default: f => ((cases.find (y => y [0] === x) || []) [1] || f) (x),
end: f => ((A.find (y => y [0] === x) (cases) || []) [1] || f) (x),
}
return o
},
match_f: x => {
const cases = []
const o = {
case: p => f => {
cases.push ([p, f])
return o
},
default: f => ((cases.find (y => y [0] (x)) || []) [1] || f) (x),
}
return o
},
P: {
try: f => ({
catch: async g => {
try {
return await f ()
}
catch (err) {
return await g (err)
}
}
}),
},
}