green_curry
Version:
Curried functional programming library
576 lines (491 loc) • 15.9 kB
JavaScript
const F = require ('./function')
const A = module.exports = {
//////////////
// //
// Arrays //
// //
//////////////
// all functions assume dense 0-indexed arrays
// 'a array -> 'a array
clone: xs => [...xs],
// 'a -> 'a array -> 'a array
cons: h => xs => [h, ...xs],
// 'a array -> 'a
head: xs => xs.length ? xs [0] : F.throw (new Error ('A.head: given an empty array')),
// 'a array -> 'a array
tail: xs => xs.length ? xs.slice (1) : F.throw (new Error ('A.tail: given an empty array')),
// 'a array -> int
length: xs => xs.length,
// 'a array -> bool
is_empty: xs => xs.length === 0,
// int -> 'a array -> 'a
get: n => xs => xs [n],
// int -> int -> int array
range: x => y => {
let l = 0
const ans = []
ans.length = y - x
for (var n = 0; n <= y - x; n++) ans [l++] = n + x
return ans
},
// int -> 'a -> 'a array
create: n => x => A.init (n) (F.const (x)),
// int -> (int -> 'a) -> 'a array
init: n => f => A.map (f) (A.range (0) (n - 1)),
// 'a array -> 'a array
rev: xs => {
const ans = []
for (var i = 0, len = ans.length = xs.length; i < len; i++) {
ans [i] = xs [len - i - 1]
}
return ans
},
// int -> int -> 'a array -> 'a array
slice: x => y => xs => xs.slice (x, y),
// (int -> 'a -> unit) -> 'a array -> unit
iteri: f => xs => xs.forEach ((x, i) => f (i) (x)),
// ('a -> unit) -> 'a array -> unit
iter: f => xs => xs.forEach (x => f (x)),
// ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a
fold: f => a => xs => xs.reduce ((a, h) => f (a) (h), a),
// ('a -> 'a -> 'a) -> 'a array -> 'a
reduce: f => xs => xs.reduce ((a, h) => f (a) (h)),
// ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a array
scan: f => a => xs =>
xs.reduce ((a, h) => {
const a2 = f (a [1]) (h)
return [[...a [0], a2], a2]
}, [[a], a]) [0],
// (int -> 'a -> 'b) -> 'a array -> 'b array
mapi: f => xs => xs.map ((x, i) => f (i) (x)),
// ('a -> 'b) -> 'a array -> 'b array
map: f => xs => xs.map (x => f (x)),
// ('a -> bool) -> 'a array -> 'a
find: f => xs => {
const ans = xs.find (x => f (x))
if (ans === undefined)
throw new Error ('A.find: element not found')
return ans
},
// ('a -> bool) -> 'a array -> 'a
try_find: f => xs => xs.find (x => f (x)),
// ('a -> bool) -> 'a array -> int
find_index: f => xs => {
const ans = xs.findIndex (x => f (x))
if (ans === -1)
throw new Error ('A.find: element not found')
return ans
},
// ('a -> bool) -> 'a array -> int
try_find_index: f => xs => {
const ans = xs.findIndex (x => f (x))
return ans !== -1 ? ans : undefined
},
// ('a -> unit/'b) -> 'a array -> unit/'b
pick: f => xs => {
let ans = undefined
xs.some (x => (ans = f (x)) !== undefined)
if (ans === undefined)
throw new Error ('A.find: element not found')
return ans
},
// ('a -> unit/'b) -> 'a array -> unit/'b
try_pick: f => xs => {
let ans = undefined
xs.some (x => (ans = f (x)) !== undefined)
return ans
},
// ('a -> bool) -> 'a array -> 'a array
filter: f => xs => xs.filter (x => f (x)),
// ('a -> bool) -> 'a array -> bool
for_all: f => xs => xs.every (x => f (x)),
// ('a -> bool) -> 'a array -> bool
exists: f => xs => xs.some (x => f (x)),
// 'a -> 'a array -> bool
contains: x => xs => xs.some (F['='] (x)),
// ('a -> 'a -> int) -> 'a array -> 'a array
sort: f => xs => [...xs].sort ((x, y) => f (x) (y)),
// ('a -> bool) -> 'a array -> ('a array * 'a array)
partition: f => xs => [xs.filter (x => f (x)), xs.filter (x => ! f (x))],
// 'a array -> 'a array
uniq: xs => {
const ans = []
xs.forEach (x => ans.some (F['='] (x)) || ans.push (x))
return ans
},
// ('a -> string) -> 'a array -> 'a array
hash_uniq: f => xs => {
const memo = {}
xs.forEach (x => {
const k = f (x)
if (memo [k] === undefined)
memo [k] = x
})
return Object.values (memo)
},
// ('a * 'b) array -> 'a array * 'b array
unzip: xs => {
var ans = [[], []]
xs.forEach ((x, i) => {
ans [0] [i] = x [0]
ans [1] [i] = x [1]
})
return ans
},
// 'a array array -> 'a array
flatten: xs => xs.flat (),
////////////////
// //
// 2 Arrays //
// //
////////////////
// 'a array -> 'a array -> 'a array
append: xs1 => xs2 => [...xs1, ...xs2],
// 'a array -> 'b array -> bool
eq_length: xs1 => xs2 => xs1.length === xs2.length,
// 'a array -> 'b array -> bool
uneq_length: xs1 => xs2 => xs1.length !== xs2.length,
// (int -> 'a -> 'b -> unit) -> 'a array -> 'b array -> unit
iteri2: f => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.iteri2: arrays do not have equal length')
for (var i = 0, len = xs1.length; i < len; i++) {
f (i) (xs1 [i]) (xs2 [i])
}
},
// ('a -> 'b -> unit) -> 'a array -> 'b array -> unit
iter2: f => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.iter2: arrays do not have equal length')
for (var i = 0, len = xs1.length; i < len; i++) {
f (xs1 [i]) (xs2 [i])
}
},
// ('a -> 'b -> 'c -> 'a) -> 'a -> 'b array -> 'c array -> 'a
fold2: f => a => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.fold2: arrays do not have equal length')
for (var i = 0, len = xs1.length; i < len; i++) {
a = f (a) (xs1 [i]) (xs2 [i])
}
return a
},
// (int -> 'a -> 'b -> 'c) -> 'a array -> 'b array -> 'c array
mapi2: f => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.mapi2: arrays do not have equal length')
const ans = []
for (var i = 0, len = xs1.length; i < len; i++) {
ans.push (f (i) (xs1 [i]) (xs2 [i]))
}
return ans
},
// ('a -> 'b -> 'c) -> 'a array -> 'b array -> 'c array
map2: f => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.map2: arrays do not have equal length')
const ans = []
for (var i = 0, len = xs1.length; i < len; i++) {
ans.push (f (xs1 [i]) (xs2 [i]))
}
return ans
},
// ('a -> 'b -> bool) -> 'a array -> 'b array -> bool
for_all2: f => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.for_all2: arrays do not have equal length')
for (var i = 0, len = xs1.length; i < len; i++) {
if (! f (xs1 [i]) (xs2 [i]))
return false
}
return true
},
// ('a -> 'b -> bool) -> 'a array -> 'b array -> bool
exists2: f => xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.exists2: arrays do not have equal length')
for (var i = 0, len = xs1.length; i < len; i++) {
if (f (xs1 [i]) (xs2 [i]))
return true
}
return false
},
// 'a array -> 'b array -> ('a * 'b) array
zip: xs1 => xs2 => {
if (xs1.length !== xs2.length)
throw new Error ('A.zip: arrays do not have equal length')
var ans = []
for (var i = 0; i < xs1.length; i++)
ans.push ([xs1[i], xs2[i]])
return ans
},
// 'a array -> 'a array -> bool
equals: xs1 => xs2 => ! A.uneq_length (xs1) (xs2) && A.for_all2 (F['=']) (xs1) (xs2),
////////////////
// //
// Promised //
// //
////////////////
P: {
//////////////
// //
// Serial //
// //
//////////////
s: {
// int -> (int -> 'a promise) -> 'a array promise
init: n => async f => {
const ans = []
for (var i = 0; i < n; i++) {
ans.push (await f (i))
}
return ans
},
// (int -> 'a -> unit promise) -> 'a array -> unit promise
iteri: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
await f (i) (xs [i])
}
},
// ('a -> unit promise) -> 'a array -> unit promise
iter: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
await f (xs [i])
}
},
// ('a -> 'b -> 'a promise) -> 'a -> 'b array -> 'a promise
fold: f => a => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
a = await f (a) (xs [i])
}
return a
},
// ('a -> 'a -> 'a promise) -> 'a array -> 'a promise
reduce: f => async xs => {
for (var i = 1, len = xs.length, a = xs [0]; i < len; i++) {
a = await f (a) (xs [i])
}
return a
},
// ('a -> 'b -> 'a promise) -> 'a -> 'b array -> 'a array promise
scan: f => a => async xs => {
const ans = [a]
for (var i = 0, len = xs.length; i < len; i++) {
ans.push (a = await f (a) (xs [i]))
}
return ans
},
// (int -> 'a -> 'b promise) -> 'a array -> 'b array promise
mapi: f => async xs => {
const ans = []
for (var i = 0, len = xs.length; i < len; i++) {
ans.push (await f (i) (xs [i]))
}
return ans
},
// ('a -> 'b promise) -> 'a array -> 'b array promise
map: f => async xs => {
const ans = []
for (var i = 0, len = xs.length; i < len; i++) {
ans.push (await f (xs [i]))
}
return ans
},
// ('a -> bool promise) -> 'a array -> 'a promise
find: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
if (await f (xs [i]))
return xs [i]
}
throw new Error ('P.A.s.find: element not found')
},
// ('a -> bool promise) -> 'a array -> 'a promise
try_find: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
if (await f (xs [i]))
return xs [i]
}
},
// ('a -> bool promise) -> 'a array -> int promise
find_index: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
if (await f (xs [i]))
return i
}
throw new Error ('P.A.s.find_index: element not found')
},
// ('a -> bool promise) -> 'a array -> int promise
try_find_index: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
if (await f (xs [i]))
return i
}
},
// ('a -> unit/'b promise) -> 'a array -> unit/'b promise
pick: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
const ans = await f (xs [i])
if (ans !== undefined)
return ans
}
throw new Error ('P.A.s.pick: element not found')
},
// ('a -> unit/'b promise) -> 'a array -> unit/'b promise
try_pick: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
const ans = await f (xs [i])
if (ans !== undefined)
return ans
}
throw new Error ('P.A.s.pick: element not found')
},
// ('a -> bool promise) -> 'a array -> 'a array promise
filter: f => async xs => {
const ans = []
for (var i = 0, len = xs.length; i < len; i++) {
if (await f (xs [i]))
ans.push (xs [i])
}
return ans
},
// ('a -> bool promise) -> 'a array -> bool promise
for_all: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
if (! (await f (xs [i])))
return false
}
return true
},
// ('a -> bool promise) -> 'a array -> bool promise
exists: f => async xs => {
for (var i = 0, len = xs.length; i < len; i++) {
if (await f (xs [i]))
return true
}
return false
},
},
////////////////
// //
// Parallel //
// //
////////////////
p: {
// int -> (int -> 'a promise) -> 'a array promise
init: n => async f => {
const ans = []
ans.length = n
return Promise.all (ans.map ((x, i) => f (i)))
},
// (int -> 'a -> unit promise) -> 'a array -> unit promise
iteri: f => async xs => {await Promise.all (xs.map ((x, i) => f (i) (x)))},
// ('a -> unit promise) -> 'a array -> unit promise
iter: f => async xs => {await Promise.all (xs.map (x => f (x)))},
// (int -> 'a -> 'b promise) -> 'a array -> 'b array promise
mapi: f => xs => Promise.all (xs.map ((x, i) => f (i) (x))),
// ('a -> 'b promise) -> 'a array -> 'b array promise
map: f => xs => Promise.all (xs.map (x => f (x))),
// ('a -> bool promise) -> 'a array -> 'a promise
find: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => await f (x) && resolve (x)))
reject (new Error (`P.A.p.find: element not found`))
}
catch (err) {
reject (err)
}
}),
// ('a -> bool promise) -> 'a array -> 'a promise
try_find: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => await f (x) && resolve (x)))
resolve ()
}
catch (err) {
reject (err)
}
}),
// ('a -> bool promise) -> 'a array -> int promise
find_index: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => await f (x) && resolve (i)))
reject (new Error (`P.A.p.find_index: element not found`))
}
catch (err) {
reject (err)
}
}),
// ('a -> bool promise) -> 'a array -> int promise
try_find_index: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => await f (x) && resolve (i)))
resolve ()
}
catch (err) {
reject (err)
}
}),
// ('a -> unit/'b promise) -> 'a array -> unit/'b promise
pick: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => {
const ans = await f (x)
if (ans !== undefined)
resolve (ans)
}))
reject (new Error (`P.A.p.pick: element not found`))
}
catch (err) {
reject (err)
}
}),
// ('a -> unit/'b promise) -> 'a array -> unit/'b promise
try_pick: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => {
const ans = await f (x)
if (ans !== undefined)
resolve (ans)
}))
resolve ()
}
catch (err) {
reject (err)
}
}),
// ('a -> bool promise) -> 'a array -> 'a array promise
filter: f => async xs =>
(await Promise.all (xs.map (async x => [await f (x), x])))
.filter (x => x [0])
.map (x => x [1]),
// ('a -> bool promise) -> 'a array -> bool promise
for_all: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => await f (x) || resolve (false)))
resolve (true)
}
catch (err) {
reject (err)
}
}),
// ('a -> bool promise) -> 'a array -> bool promise
exists: f => xs =>
new Promise (async (resolve, reject) => {
try {
await Promise.all (xs.map (async x => await f (x) && resolve (true)))
resolve (false)
}
catch (err) {
reject (err)
}
}),
},
},
}