Skip to content

Instantly share code, notes, and snippets.

@Trung0246
Last active November 21, 2024 05:36
Show Gist options
  • Select an option

  • Save Trung0246/a7bd3d3a846d49c588363c552c4faecb to your computer and use it in GitHub Desktop.

Select an option

Save Trung0246/a7bd3d3a846d49c588363c552c4faecb to your computer and use it in GitHub Desktop.
Functional in JS
(function() {
var ff = {};
ff._ = Symbol("_");
ff._$ = Symbol("_$");
function thunk(fn) {
fn[ff._$] = true;
return fn;
}
function array_popf(chk_fn, array) {
let shift = 0;
for (let i = 0; i < array.length; ++i) {
if (chk_fn(array[i], i, array))
++ shift;
else if (shift > 0)
array[i - shift] = array[i];
}
array.length -= shift;
return array;
}
function array_lenf(chk_fn, array) {
let i = array.length, count = 0;
while(i--) count += !!chk_fn(array[i]);
return count;
}
function array_pushf(fn, arr1, arr2) {
let count = 0;
for (let i = 0; i < arr1.length && count < arr2.length; ++ i)
if (fn(arr1[i]))
arr1[i] = arr2[count++];
while (count < arr2.length)
arr1.push(arr2[count++]);
return arr1;
}
function array_pushi(array, elem, index) {
for (let i = array.length - 1; i >= index; --i)
array[i + elem.length] = array[i];
for (let i = 0; i < elem.length; ++i)
array[index + i] = elem[i];
return index + elem.length;
}
function curry(ctx, arg_curr, flag, ...arg_next) {
array_pushf(_ => _ === ff._, arg_curr = [].concat(arg_curr), arg_next);
while (!arg_curr.includes(ff._) && arg_curr.length >= ctx.length) {
ctx = ctx(...arg_curr.splice(0, ctx.length));
if (!(ctx instanceof Function) || (!flag && ctx instanceof Function && !ctx[ff._$]))
return ctx;
if (ctx instanceof Function && ctx.length === 0 && ctx[ff._$])
return ctx();
}
const c = curry.bind(null, ctx, arg_curr, flag);
c[ff._$] = true;
return c;
}
ff.P = (fn, ...args) => curry(fn, args, true);
ff.F = (fn, ...args) => {
fn[ff._$] = true;
if (args.length === 0) {
const ctx = () => curry.bind(null, fn, [], false);
return (...args_next) => ctx()(...args_next);
}
return curry.bind(null, fn, args, false);
};
ff.C = ff.F((fn1, fn2, arg) => fn1(fn2(arg))); // Compose
ff.H = ff.F((fn1, fn2, arg) => fn1(fn2(arg))(arg)); // Ramda.chain
ff.K = ff.F((f, g, _) => ff.C(ff.flat_map(f, new _.constructor()), g)(_)); // Kleisli composition
ff.id = ff.F(arg => arg);
ff.cond = ff.F((a,b,c,_)=>thunk(a(_)?()=>b(_):()=>c(_)));
ff.key = ff.F((inst) => Array.isArray(inst) || inst?.constructor === String || (Number.isFinite(inst?.length) && inst?.[ff._$]) ? [...Array.prototype.keys.call(inst)] : Object.keys(inst));
ff.fold = ff.F((fn, right, init, inst) => (keys =>
ff.trampoline(ff.Y(recurse =>
ff.cond(idx => right ? idx < 0 : idx === keys.length)
(idx => init)
(idx => (init = fn(init, inst[keys[idx]], keys[idx], inst), recurse(idx + (right ? -1 : 1))))
))(right ? keys.length - 1 : 0)
)(ff.key(inst)));
ff.map = ff.F((fn, inst) =>
ff.fold(
(temp, val, key) => (temp[key] = fn(val, key, inst), temp),
false, inst, inst
));
ff.flat_map = ff.F((fn, init, inst) => // Known as bind, chain, fold_map, or >>=
ff.fold(
(temp, val, key) => ff.cons(temp)(fn(val, key, inst)),
false, init, inst
));
ff.flat_map_mut = ff.F((fn, inst) =>
ff.trampoline(ff.Y(recurse =>
ff.cond(idx => idx === inst.length)
(_ => inst)
(idx => {
const val = inst[idx];
ff.array_popf((_, i) => i === idx, inst);
return recurse(ff.array_pushi(inst, fn(val, idx, inst), idx));
})
))(0));
ff.instof = ff.F((fn, inst) => inst instanceof fn);
ff.typeof = ff.F((fn, inst) => typeof inst === fn);
ff.try = ff.F((fn_body, fn_catch, _) => {
try {
return fn_body(_);
} catch (e) {
return fn_catch(e);
}
});
ff.string_is_upper_case = ff.F(char => char === char.toUpperCase() && char !== char.toLowerCase());
ff.string_is_lower_case = ff.F(char => char === char.toLowerCase() && char !== char.toUpperCase());
ff.string_split_camel_boundary = ff.F(str =>
ff.fold(
(boundaries, char, idx) =>
ff.cond(() => idx > 0 && ff.string_is_upper_case(char) && ff.string_is_lower_case(str[idx - 1]))
(() => (boundaries.push(idx), boundaries))
(ff.cond(() => idx > 1 && ff.string_is_lower_case(char) && ff.string_is_upper_case(str[idx - 1]) && ff.string_is_upper_case(str[idx - 2]))
(() => (boundaries.push(idx - 1), boundaries))
(() => boundaries))
(null),
false, [], str
));
ff.split_at = ff.F((boundaries, inst) =>
ff.cond(() => boundaries.length === 0)
(() => [inst])
(() => ff.fold(
(acc, boundary, idx, arr) =>
ff.cond(start => boundary > start)
(start => [...acc, inst.slice(start, boundary)])
(_ => acc)
(idx > 0 ? arr[idx - 1] : 0),
false, [], [...boundaries, inst.length]
))
(null));
ff.string_split_camel = ff.F(str =>
ff.cond(() => !str)
(() => [])
(ff.split_at(ff.string_split_camel_boundary(str)))
(str));
ff.pure = ff.F(inst => [inst]);
ff.head = ff.F(arr => arr[0]);
ff.tail = ff.F(arr => arr[arr.length - 1]);
ff.init = ff.F(arr => arr.slice(0, -1));
ff.rest = ff.F(arr => arr.slice(1));
ff.join = ff.F((sep, arr) => arr.join(sep));
ff.reverse = ff.F(arr => arr.reverse());
ff.array_popf = ff.F(array_popf);
ff.array_lenf = ff.F(array_lenf);
ff.array_pushf = ff.F(array_pushf);
ff.array_pushi = ff.F(array_pushi);
ff.flip = ff.F((fn, a, b, c) => fn(b)(a)(c));
ff.inv = ff.F((fn, a, b, c) => fn(c)(b)(a));
ff.rotl = ff.F((fn, a, b, c) => fn(b)(c)(a));
ff.rotr = ff.F((fn, a, b, c) => fn(c)(a)(b));
ff.swap = ff.F((fn, a, b) => fn(b)(a));
ff.at = ff.F((i, inst) => inst?.[i]);
ff.path = ff.swap(ff.fold(ff.swap(ff.at), false));
ff.ap = ff.F((fns, full, init, insts) =>
ff.fold((acc, fn) => ff.cons(acc, ff.map(ff.ary(full ? 3 : 1, fn), insts)), false, init, fns)
);
ff.lift = ff.F((fn, full, init, insts) =>
ff.fold(ff.ap(ff._, full, init), false, ff.map(ff.ary(full ? 3 : 1, ff.F(fn)), ff.head(insts)), ff.rest(insts))
);
ff.ary = ff.F((n, fn) => thunk(() => new Proxy(ary(n, false, fn), {
get: (target, prop) => prop === "length" ? n : Reflect.get(target, prop)
})));
ff.trampoline = fn => (...args) => {
let ctx = fn;
while (ctx instanceof Function && ctx[ff._$])
ctx = ctx(...args);
return ctx;
};
ff.Y = fn => thunk((x => x(x))(g => fn((...args) => thunk(() => g(g)(...args)))));
// Monoid
ff.cons = ff.F((a, b) => {
b = ff.trampoline(b)();
return b?.constructor.prototype.concat ? ff.trampoline(a)().concat(b) :
b?.constructor === Object ? Object.assign(new b.constructor(), ff.trampoline(a)(), b) :
typeof b === "boolean" ? a && b :
typeof b === "number" ? a + b :
a;
});
const af = [];
function ary(n, t, fn) {
if (!af[n]) {
const arg = Array.from({ length: n }, (_, i) => `_${i}`);
af[n] = [
new Function(...arg, `return this(${arg.join(",")})`),
new Function("__", ...arg, `return this.call(__,${arg.join(",")})`)
]
}
return af[n][~~t].bind(fn);
}
const blacklist = [
["WritableStreamDefaultWriter", "closed"],
["WritableStreamDefaultWriter", "ready"],
["ViewTransition", "finished"],
["ViewTransition", "ready"],
["ViewTransition", "updateCallbackDone"],
["ReadableStreamDefaultReader", "closed"],
["ReadableStreamBYOBReader", "closed"],
["PromiseRejectionEvent", "promise"],
["NavigationTransition", "finished"],
["FontFace", "loaded"],
["BeforeInstallPromptEvent", "userChoice"],
["Animation", "finished"],
["Animation", "ready"],
["BackgroundFetchRecord", "responseReady"],
["WebSocketStream", "opened"],
["WebSocketStream", "closed"],
["MediaKeySession", "closed"],
["FontFaceSet", "ready"],
["ServiceWorkerContainer", "ready"],
["WebTransport", "ready"],
["WebTransport", "closed"],
["RTCPeerConnection", "peerIdentity"]
];
ff.impl = ff.F((path_fn, chk_fn, key_fn, flag, cls_impl) =>
ff.cond(path_fn)
(cls => ff.fold((acc, mth) =>
ff.try(
ff.cond(chk_fn)
(([cls, mth]) => (acc[key_fn(cls, mth)] = ff.F(ary(path_fn(cls)[mth].length, flag, path_fn(cls)[mth])), acc))
(_ => acc),
_ => acc
)([cls, mth]),
false, {}, Object.getOwnPropertyNames(path_fn(cls))))
(_ => {})
(cls_impl)
);
const snake_case = ff.C(ff.join("_"), ff.C(ff.map(Function.prototype.call.bind(String.prototype.toLowerCase)), ff.string_split_camel));
const glb_impl_name = (cls, mth) => snake_case(cls) + "_" + snake_case(mth);
Object.assign(ff, ff.fold(
(acc, class_name) => ({
...acc,
...ff.impl(
cls => ff.path([cls, "prototype"], globalThis),
([cls, mth]) => !blacklist.some(([c, m]) => c === cls && m === mth) && ff.try(cls => ff.instof(Function)(ff.path([cls, "prototype", mth], globalThis)))(_ => false)(cls),
glb_impl_name,
true,
class_name
),
...ff.impl(
cls => globalThis[cls],
([cls, mth]) => ff.instof(Function)(globalThis[cls][mth]),
glb_impl_name,
false,
class_name
)
}), false, {},
ff.fold((acc, key) => (key[0] === key[0].toUpperCase() ? ff.cons(acc)([key]) : acc), false, [], Object.getOwnPropertyNames(globalThis))
));
globalThis.ff = ff;
})();
(function(){function l(b){b[a._$]=!0;return b}function q(b,c,d){let e=0;for(let f=0;f<c.length&&e<d.length;++f)b(c[f])&&(c[f]=d[e++]);for(;e<d.length;)c.push(d[e++]);return c}function m(b,c,d,...e){for(q(f=>f===a._,c=[].concat(c),e);!c.includes(a._)&&c.length>=b.length;){b=b(...c.splice(0,b.length));if(!(b instanceof Function)||!d&&b instanceof Function&&!b[a._$])return b;if(b instanceof Function&&b.length===0&&b[a._$])return b()}b=m.bind(null,b,c,d);b[a._$]=!0;return b}function r(b, c,d){if(!n[b]){const e=Array.from({length:b},(f,h)=>`_${h}`);n[b]=[new Function(...e,`return this(${e.join(",")})`),new Function("__",...e,`return this.call(__,${e.join(",")})`)]}return n[b][~~c].bind(d)}var a={};a._=Symbol("_");a._$=Symbol("_$");a.P=(b,...c)=>m(b,c,!0);a.F=(b,...c)=>{b[a._$]=!0;return c.length===0?(...d)=>m.bind(null,b,[],!1)(...d):m.bind(null,b,c,!1)};a.C=a.F((b,c,d)=>b(c(d)));a.H=a.F((b,c,d)=>b(c(d))(d));a.K=a.F((b,c,d)=>a.C(a.flat_map(b,new d.constructor),c)(d));a.id=a.F(b=>b); a.cond=a.F((b,c,d,e)=>l(b(e)?()=>c(e):()=>d(e)));a.key=a.F(b=>Array.isArray(b)||b?.constructor===String||Number.isFinite(b?.length)&&b?.[a._$]?[...Array.prototype.keys.call(b)]:Object.keys(b));a.fold=a.F((b,c,d,e)=>(f=>a.trampoline(a.Y(h=>a.cond(g=>c?g<0:g===f.length)(g=>d)(g=>(d=b(d,e[f[g]],f[g],e),h(g+(c?-1:1))))))(c?f.length-1:0))(a.key(e)));a.map=a.F((b,c)=>a.fold((d,e,f)=>(d[f]=b(e,f,c),d),!1,c,c));a.flat_map=a.F((b,c,d)=>a.fold((e,f,h)=>a.cons(e)(b(f,h,d)),!1,c,d));a.flat_map_mut=a.F((b,c)=>a.trampoline(a.Y(d=>a.cond(e=>e===c.length)(e=>c)(e=>{const f=c[e];a.array_popf((h,g)=>g===e,c);return d(a.array_pushi(c,b(f,e,c),e))})))(0));a.instof=a.F((b,c)=>c instanceof b);a.typeof=a.F((b,c)=>typeof c===b);a.try=a.F((b,c,d)=>{try{return b(d)}catch(e){return c(e)}});a.string_is_upper_case=a.F(b=>b===b.toUpperCase()&&b!==b.toLowerCase());a.string_is_lower_case=a.F(b=>b===b.toLowerCase()&&b!==b.toUpperCase());a.string_split_camel_boundary=a.F(b=>a.fold((c,d,e)=>a.cond(()=>e>0&&a.string_is_upper_case(d)&& a.string_is_lower_case(b[e-1]))(()=>(c.push(e),c))(a.cond(()=>e>1&&a.string_is_lower_case(d)&&a.string_is_upper_case(b[e-1])&&a.string_is_upper_case(b[e-2]))(()=>(c.push(e-1),c))(()=>c))(null),!1,[],b));a.split_at=a.F((b,c)=>a.cond(()=>b.length===0)(()=>[c])(()=>a.fold((d,e,f,h)=>a.cond(g=>e>g)(g=>[...d,c.slice(g,e)])(g=>d)(f>0?h[f-1]:0),!1,[],[...b,c.length]))(null));a.string_split_camel=a.F(b=>a.cond(()=>!b)(()=>[])(a.split_at(a.string_split_camel_boundary(b)))(b));a.pure=a.F(b=>[b]);a.head=a.F(b=> b[0]);a.tail=a.F(b=>b[b.length-1]);a.init=a.F(b=>b.slice(0,-1));a.rest=a.F(b=>b.slice(1));a.join=a.F((b,c)=>c.join(b));a.reverse=a.F(b=>b.reverse());a.array_popf=a.F(function(b,c){let d=0;for(let e=0;e<c.length;++e)b(c[e],e,c)?++d:d>0&&(c[e-d]=c[e]);c.length-=d;return c});a.array_lenf=a.F(function(b,c){let d=c.length,e=0;for(;d--;)e+=!!b(c[d]);return e});a.array_pushf=a.F(q);a.array_pushi=a.F(function(b,c,d){for(var e=b.length-1;e>=d;--e)b[e+c.length]=b[e];for(e=0;e<c.length;++e)b[d+e]=c[e];return d+ c.length});a.flip=a.F((b,c,d,e)=>b(d)(c)(e));a.inv=a.F((b,c,d,e)=>b(e)(d)(c));a.rotl=a.F((b,c,d,e)=>b(d)(e)(c));a.rotr=a.F((b,c,d,e)=>b(e)(c)(d));a.swap=a.F((b,c,d)=>b(d)(c));a.at=a.F((b,c)=>c?.[b]);a.path=a.swap(a.fold(a.swap(a.at),!1));a.ap=a.F((b,c,d,e)=>a.fold((f,h)=>a.cons(f,a.map(a.ary(c?3:1,h),e)),!1,d,b));a.lift=a.F((b,c,d,e)=>a.fold(a.ap(a._,c,d),!1,a.map(a.ary(c?3:1,a.F(b)),a.head(e)),a.rest(e)));a.ary=a.F((b,c)=>l(()=>new Proxy(r(b,!1,c),{get:(d,e)=>e==="length"?b:Reflect.get(d,e)}))); a.trampoline=b=>(...c)=>{let d=b;for(;d instanceof Function&&d[a._$];)d=d(...c);return d};a.Y=b=>l((c=>c(c))(c=>b((...d)=>l(()=>c(c)(...d)))));a.cons=a.F((b,c)=>{c=a.trampoline(c)();return c?.constructor.prototype.concat?a.trampoline(b)().concat(c):c?.constructor===Object?Object.assign(new c.constructor,a.trampoline(b)(),c):typeof c==="boolean"?b&&c:typeof c==="number"?b+c:b});const n=[],v=[["WritableStreamDefaultWriter","closed"],["WritableStreamDefaultWriter","ready"],["ViewTransition","finished"], ["ViewTransition","ready"],["ViewTransition","updateCallbackDone"],["ReadableStreamDefaultReader","closed"],["ReadableStreamBYOBReader","closed"],["PromiseRejectionEvent","promise"],["NavigationTransition","finished"],["FontFace","loaded"],["BeforeInstallPromptEvent","userChoice"],["Animation","finished"],["Animation","ready"],["BackgroundFetchRecord","responseReady"],["WebSocketStream","opened"],["WebSocketStream","closed"],["MediaKeySession","closed"],["FontFaceSet","ready"],["ServiceWorkerContainer", "ready"],["WebTransport","ready"],["WebTransport","closed"],["RTCPeerConnection","peerIdentity"]];a.impl=a.F((b,c,d,e,f)=>a.cond(b)(h=>a.fold((g,w)=>a.try(a.cond(c)(([k,p])=>(g[d(k,p)]=a.F(r(b(k)[p].length,e,b(k)[p])),g))(k=>g),k=>g)([h,w]),!1,{},Object.getOwnPropertyNames(b(h))))(h=>{})(f));const t=a.C(a.join("_"),a.C(a.map(Function.prototype.call.bind(String.prototype.toLowerCase)),a.string_split_camel)),u=(b,c)=>t(b)+"_"+t(c);Object.assign(a,a.fold((b,c)=>({...b,...a.impl(d=>a.path([d,"prototype"], globalThis),([d,e])=>!v.some(([f,h])=>f===d&&h===e)&&a.try(f=>a.instof(Function)(a.path([f,"prototype",e],globalThis)))(f=>!1)(d),u,!0,c),...a.impl(d=>globalThis[d],([d,e])=>a.instof(Function)(globalThis[d][e]),u,!1,c)}),!1,{},a.fold((b,c)=>c[0]===c[0].toUpperCase()?a.cons(b)([c]):b,!1,[],Object.getOwnPropertyNames(globalThis))));globalThis.ff=a})();
var is_even = ff.trampoline(ff.Y(fn => n =>
ff.cond(x => x === 0)
(() => true)
(ff.cond(x => x === 1)
(() => false)
(x => fn(x - 2)))
(n)
));
console.log(is_even(1000000)); // true
console.log(is_even(1000001)); // false
console.log(
ff.C(
ff.swap(ff.array_join)(". "),
ff.C(
ff.swap(ff.array_map)(ff.C(ff.string_to_upper_case, ff.head)),
ff.inv(ff.string_split)(-1)(" ")
)
)("hunter stockton thompson")
); // "H. S. T"
console.log(
ff.C(ff.join("_"), ff.C(ff.map(ff.string_to_lower_case), ff.string_split_camel))
("OffscreenCanvasRenderingContext2D")
); // "offscreen_canvas_rendering_context2d"
console.log(
ff.C(ff.C(ff._, ff.swap(ff.array_map)), ff.C(ff.C, ff.swap(ff.array_filter)))
(x => x % 2 === 0)(x => x * 3)([1,2,3,4,5,6,7,8,9,10])
); // [6,12,18,24,30]
var chunk = size =>
ff.trampoline(ff.Y(fn => arr =>
ff.cond(arr => arr.length === 0)
(ff.id)
(arr => ff.cons([ff.rotr(ff.array_slice)(0, size)(arr)])(fn(ff.rotr(ff.array_slice)(size, Infinity)(arr))))
(arr)
));
console.log(chunk(3)([1,2,3,4,5,6,7,8,9,10])); // [[1,2,3],[4,5,6],[7,8,9],[10]]
var chunk_proc = size =>
ff.C(
ff.swap(ff.array_map)(
ff.C(
ff.swap(ff.number_to_string)(10),
ff.swap(ff.array_reduce)((acc, val) => acc + val)
)
),
chunk(size)
);
console.log(chunk_proc(3)([1,2,3,4,5,6,7,8,9,10])); // ["6","15","24","10"]
// Special stack that instead push new value it check if top are equal to new value then increment it.
var stk_push = ff.F((stk, val) =>
ff.cond(() => ff.tail(stk)?.val === val)
(() => (++ff.tail(stk).num, stk))
(() => (stk.push({val: val, num: 0}), stk))
(null)
);
var stk_pull = ff.F(stk =>
ff.cond(() => (ff.tail(stk)?.num ?? 0) > 0)
(() => (--ff.tail(stk).num, ff.tail(stk).val))
(() => stk.pop()?.val)
(null)
);
var ack = ff.trampoline(
ff.Y(fn => ([m, n, stk = []]) =>
ff.cond(([m, n, stk]) => m === 0)
(ff.cond(() => stk.length === 0)
(([m, n, stk]) => n + 1)
(([m, n, stk]) => fn([stk_pull(stk), n + 1, stk])))
(ff.cond(([m, n, stk]) => n === 0)
(([m, n, stk]) => fn([m - 1, 1, stk]))
(([m, n, stk]) => fn([m, n - 1, stk_push(stk, m - 1)])))
([m, n, stk])
));
console.log(ack([3, 6])); // 509
ff.add = ff.F((a, b) => a + b);
ff.sub = ff.F((a, b) => a - b);
ff.mul = ff.F((a, b) => a * b);
ff.pow = ff.F((a, b) => a ** b);
ff.div = ff.F((a, b) => a / b);
ff.rem = ff.F((a, b) => a % b);
ff.mod = ff.F((a, b) => (a % b + b) % b);
ff.eq = ff.F((a, b) => a === b);
ff.ne = ff.F((a, b) => a !== b);
ff.gt = ff.F((a, b) => a > b);
ff.ge = ff.F((a, b) => a >= b);
ff.lt = ff.F((a, b) => a < b);
ff.le = ff.F((a, b) => a <= b);
ff.and = ff.F((a, b) => a && b);
ff.or = ff.F((a, b) => a || b);
ff.xor = ff.F((a, b) => a ^ b);
ff.not = ff.F(a => !a);
ff.bit_and = ff.F((a, b) => a & b);
ff.bit_or = ff.F((a, b) => a | b);
ff.bit_xor = ff.F((a, b) => a ^ b);
ff.bit_not = ff.F(a => ~a);
ff.bit_shl = ff.F((a, b) => a << b);
ff.bit_shr = ff.F((a, b) => a >> b);
ff.bit_sar = ff.F((a, b) => a >>> b);
// Remainder vs Modulus with same arg but different result
console.log(ff.rem(-7)(3)); // -1
console.log(ff.mod(-7)(3)); // 2
var quadratic = ff.F((a, b, c) => ff.C(
(d) => [(-b + d) / (2 * a), (-b - d) / (2 * a)],
() => ff.math_sqrt(b * b - 4 * a * c)
)(a, b, c));
console.log(quadratic(1)(-5)(6)); // [3, 2]
console.log(quadratic(2)(-7)(3)); // [3, 0.5]
console.log(ff.lift((a, b, c) => a + b + c, false, [], [[100, 200], [10, 20, 30], [1, 2, 3]]));
console.log(ff.lift((a, ak, ai, b, bk, bi, c, ck, ci) => a + b + c, true, [], [[100, 200], [10, 20, 30], [1, 2, 3]]));
// [111, 112, 113, 121, 122, 123, 131, 132, 133, 211, 212, 213, 221, 222, 223, 231, 232, 233]
console.log(ff.map(x => x * 2)([1, 2, 3, 4, 5]));
console.log(ff.map(x => x.toUpperCase())({a: 'hello', b: 'world'}));
console.log(ff.fold((acc, val) => acc + val, false, 0)([1, 2, 3, 4, 5]));
console.log(ff.fold((acc, val, key) => ff.cons(acc)({[key]: val * 2}), false, {}, {a: 1, b: 2, c: 3}));
console.log(ff.fold((acc, val) => acc + val, false, "")({a: 'hello', b: 'world'}));
console.log(ff.fold((acc, val) => acc + val, true, "")({a: 'hello', b: 'world'}));
console.log(ff.fold(ff.add, false, 0)(new Array(1000).fill(1.1))); // 1100
console.log(ff.fold((acc, val) => ff.cons(acc)([ff.path(val, globalThis)]), false, [])([['OfflineAudioContext', "prototype", 'resume']]));
// [function resume() { [native code] }]
console.log(ff.flat_map(_ => [_, _ + 2], [])([123, 234, 456])); // [123, 125, 234, 236, 456, 458]
console.log(ff.flat_map((val, key) => ({[key]: val, [key + "_"]: val + 2}), {})({a: 123, b: 234, c: 345})); // {a: 123, a_: 125, b: 234, b_: 236, c: 345, c_: 347}
console.log(ff.flat_map(x => x > 5 ? "Big!" : "Small.", "", [3,6,4,8]));
console.log(ff.flat_map(x => [x], [], [1,2,3]));
console.log(ff.flat_map(x => x % 2 === 0 ? true : false, true, [1,2,3,4]));
console.log(ff.flat_map(x => x % 2 === 0 ? true : false, true, [2,4,6,8]));
console.log(ff.K(
({a, b}) => ({a: a + b, b: a - b}),
({a, b}) => [{a: a * b, b: a / b}]
)({a: 2, b: 3})); // {a: 6.666666666666667, b: 5.333333333333333}
console.log(ff.K(
_ => [_ * 2],
ff.K(
_ => [_ * 2],
_ => [_ * 2]
)
)([5])); // [40]. Could make it works to accept "5" instead of "[5]".
console.log(ff.H(v => k => [v, k, 5], v => [v, 0])([1, 2, 3])); // [[[1,2,3],0],[1,2,3],5]
ff.S = ff.F((right, inst) => // Sequence
ff.fold(
(acc, mx) =>
ff.flat_map(
xs => ff.flat_map(
x => [ff.cons(xs)([x])],
[], mx
),
[], acc
),
right, [[]], inst
));
console.log(ff.S(false, [[1, 2], [3, 4], [5, 6]]));
ff.SA = ff.F((right, inst) => // SequenceA
ff.fold(
(acc, i) =>
ff.cons(acc)([[...ff.fold(
(inner_acc, inner_inst) =>
ff.cons(inner_acc)([ff.at(i)(inner_inst)]),
right, [], inst
)]]),
right, [], [...ff.key(ff.head(inst))]
));
console.log(ff.SA(false, [[1, 2], [3, 4], [5, 6]]));
ff.SB = ff.flat_map(inner_inst => ff.map(x => [x])(inner_inst), []); // SequenceB
ff.SB([[1, 2], [3, 4]]); // [[1],[2],[3],[4]]
ff.fold_m = ff.F((fn, right, init, inst) =>
ff.fold(
(macc, x, i) => ff.flat_map(
acc => fn(acc, x, i),
[], macc
), right, [init], inst
));
console.log(ff.fold_m((acc, x) => [acc + x, x], false, 0, [1, 2, 3])); // [6,3,5,3,6,3,5,3]
console.log(ff.fold_m((acc, x) => x % 2 === 0 ? [acc + x] : [], false, 0, [2, 4, 6])); // [12]
console.log(ff.fold_m((acc, x) => x % 2 === 0 ? [acc + x] : [], false, 0, [2, 4, 6, 7])); // []
ff.filter = ff.F((fn, right, inst) =>
ff.fold(
(filtered, val) =>
fn(val) ? ff.cons(filtered)([val]) : filtered,
right, [], inst
));
ff.filter_m = ff.F((fn, right, inst) =>
ff.fold_m(
(filtered, val) =>
ff.flat_map(
ff.C(
ff.pure,
ff.cond(ff.id)
(() => ff.cons(filtered)([val]))
(() => filtered)
),
[], fn(val)
),
right, [], inst
)
);
console.log(ff.filter(x => x % 2 === 0, false, [1, 2, 3, 4, 5, 6, 7, 8])); // [2,4,6,8]
console.log(ff.filter_m(x => [true, false], false)([1, 2, 3])); // [[1,2,3],[1,3],[1,2],[1],[2,3],[2],[3],[]]
console.log(ff.filter_m(x => [x % 2 === 0, x % 3 === 0], false)([...Array.prototype.keys.call({length: 8})]));
ff.map_m = ff.F((fn, inst) =>
ff.fold_m(
(temp, val, key) => ff.flat_map(
inner_temp => [[...temp, inner_temp]],
[], fn(val, key)
),
false, new inst.constructor(), inst
));
console.log(ff.map_m(x => [x, x * 2], [1, 2, 3]));
ff.dupe = ff.F((n, m) => ff.map(_ => m, Array(n).fill(0)));
ff.dupe_m = ff.F((n, m) => ff.map_m(_ => m, Array(n).fill(0))); // ReplicateM
console.log(ff.dupe(3, [1, 2, 3]));
console.log(ff.dupe_m(3, [1, 2, 3]));
ff.zip = ff.F((fn, xs, ys) => // zipWith
ff.map((_, idx) => fn(xs[idx], ys[idx]), Array(Math.min(xs.length, ys.length)).fill(0)));
ff.zip_m = ff.F((fn, xs, ys) => // zipWithM
ff.map_m((_, idx) => fn(xs[idx], ys[idx]), Array(Math.min(xs.length, ys.length)).fill(0)));
console.log(ff.zip((x, y) => [x, y], [1, 2, 3], [4, 5, 6]));
console.log(ff.zip_m((x, y) => [x, y], [1, 2, 3], [4, 5, 6]));
@Trung0246
Copy link
Author

Trung0246 commented Nov 21, 2024

This meant to be educational, not very performant.

MIT license.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment