我有一个可变参数的lifting函数,允许扁平的monadic链,而不需要深嵌套的函数组合:
const varArgs = f => {
const go = args =>
Object.defineProperties(
arg => go(args.concat(arg)), {
"runVarArgs": {get: function() {return f(args)}, enumerable: true},
[TYPE]: {value: "VarArgs", enumerable: true}
});
return go([]);
};
const varLiftM = (chain, of) => f => { // TODO: replace recursion with a fold
const go = (ms, g, i) =>
i === ms.length
? of(g)
: chain(ms[i]) (x => go(ms, g(x), i + 1));
return varArgs(ms => go(ms, f, 0));
};
它是可行的,但我希望通过折叠来抽象递归。普通的折叠似乎不起作用,至少与Task
类型不兼容,
const varLiftM = (chain, of) => f =>
varArgs(ms => of(arrFold(g => mx => chain(mx) (g)) (f) (ms))); // A
因为在 A
行的代数运算将会返回每次迭代的 Task
而非部分应用函数。
我该如何使用 fold 替换尾递归?
下面是当前递归实现的工作示例:
const TYPE = Symbol.toStringTag;
const struct = type => cons => {
const f = x => ({
["run" + type]: x,
[TYPE]: type,
});
return cons(f);
};
// variadic argument transformer
const varArgs = f => {
const go = args =>
Object.defineProperties(
arg => go(args.concat(arg)), {
"runVarArgs": {get: function() {return f(args)}, enumerable: true},
[TYPE]: {value: "VarArgs", enumerable: true}
});
return go([]);
};
// variadic monadic lifting function
const varLiftM = (chain, of) => f => { // TODO: replace recursion with a fold
const go = (ms, g, i) =>
i === ms.length
? of(g)
: chain(ms[i]) (x => go(ms, g(x), i + 1));
return varArgs(ms => go(ms, f, 0));
};
// asynchronous Task
const Task = struct("Task") (Task => k => Task((res, rej) => k(res, rej)));
const tOf = x => Task((res, rej) => res(x));
const tMap = f => tx =>
Task((res, rej) => tx.runTask(x => res(f(x)), rej));
const tChain = mx => fm =>
Task((res, rej) => mx.runTask(x => fm(x).runTask(res, rej), rej));
// mock function
const delay = (ms, x) =>
Task(r => setTimeout(r, ms, x));
// test data
const tw = delay(100, 1),
tx = delay(200, 2),
ty = delay(300, 3),
tz = delay(400, 4);
// specialization through partial application
const varAsyncSum =
varLiftM(tChain, tOf) (w => x => y => z => w + x + y + z);
// MAIN
varAsyncSum(tw) (tx) (ty) (tz)
.runVarArgs
.runTask(console.log, console.error);
console.log("1 sec later...");
[编辑] 根据评论中的要求,我实现了折叠功能:
const arrFold = alg => zero => xs => {
let acc = zero;
for (let i = 0; i < xs.length; i++)
acc = alg(acc) (xs[i], i);
return acc;
};
ms
、g
、x
、f
、fm
、of
、tx
、res
和rej
这样的名称,然后再同时使用它们(几乎是一起使用)对我来说看起来像是不必要的缩减,除非你知道它是关于什么的,否则会使代码变得更难以阅读。 - XCSi
一样。f
表示函数,g
也是函数(在字母表中位于f
之后,类似于在声明i
之后使用j
作为计数器),fm
表示返回单子的函数,ms
表示多个单子,因此为数组,of
表示将类型提升为单子的函数。x
是输入变量的通用名称。res
和rej
表示结果(成功)和拒绝(失败)。tx
、ty
和tz
表示任务1、任务2和任务3。 - VLAZvarLiftM
。要完成@VLAZ的名称传奇:“mx”代表单子值,“ms”代表单子值,“fm”代表单子操作(返回单子的函数)。 “tx”表示一个被封装在类型中的值,没有说明任何约束条件(即它是否单子、适用、functorial、遍历、foldable等)。 - user5536315Object.assign
,但是这会在复制过程中严格调用 getter... 感谢您提供的属性描述符提示,问题已经解决。 - user5536315