Last active
November 21, 2024 05:36
-
-
Save Trung0246/a7bd3d3a846d49c588363c552c4faecb to your computer and use it in GitHub Desktop.
Functional in JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (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; | |
| })(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (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])); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This meant to be educational, not very performant.
MIT license.