我有一个Task
,它基本上是带有错误情况并入的继续类型,而Optional
表示可能不会产生结果的计算。
[Option
到Task
似乎是自然的转变,反之亦然:
const optTask = tx =>
match(tx, {
None: () => Task((res, rej) => rej()),
Some: ({some}) => Task(res => res(some))
});
const tOption = tx =>
tx.task(x => Some(x), _ => None);
optMap(x => x + 1) (tOption(Task(res => res(5)))); // Some(6)
optMap(x => x + 1) (tOption(Task((res, rej) => rej()))); // None
// I can transform back and forth and it still works as expeced:
optMap(x => x + 1) (tOption(optTask(Some(5)))); // Some(6)
optMap(x => x + 1) (tOption(optTask(None))); // None
但是,Task
用于异步计算,这破坏了自然转换:
const delay = f => ms => x =>
Task((res, rej) => setTimeout(x => res(f(x)), ms, x));
optMap(x => x + 1) (tOption(delay(x => log(x)) (1000) (5))); // type error
我知道为什么它不起作用。使我感到困惑的是,一旦Task
成为转换的源头(并且渐进性开始发挥作用),它们似乎根本就不是自然转换。但是也许我的实现是完全错误的。
这里是完整的代码:
/***[ Auxiliary ]*************************************************************/
const record = (type, o) =>
(o[type.name || type] = type.name || type, o);
const union = type => (tag, o) =>
(o[type] = type, o.tag = tag.name || tag, o);
const match = (tx, o) =>
o[tx.tag] (tx);
const thisify = f => f({});
const log = x => (console.log(x), x);
const id = x => x;
/***[ Task ]******************************************************************/
const Task = task => record(
Task,
thisify(o => {
o.task = (res, rej) =>
task(x => {
o.task = k_ => k_(x);
return res(x);
}, rej);
return o;
}));
// functor
const tMap = f => tx =>
Task((res, rej) => tx.task(x => res(f(x)), rej));
const delay = f => ms => x =>
Task((res, rej) => setTimeout(x => res(f(x)), ms, x));
/***[ Option ]****************************************************************/
const Option = union("Option");
const None = Option("None", {});
const Some = some => Option(Some, {some});
// functor
const optMap = f => tx =>
match(tx, {
None: _ => None,
Some: ({some: x}) => Some(f(x))
});
/***[ Natural transformations ]***********************************************/
const optTask = tx =>
match(tx, {
None: () => Task((res, rej) => rej()),
Some: ({some}) => Task(res => res(some))
});
const tOption = tx =>
tx.task(x => Some(x), _ => None);
/***[ Main ]******************************************************************/
const a = optMap(x => x + 1) (tOption(Task(res => res(5))));
const b = optMap(x => x + 1) (tOption(Task((res, rej) => rej())));
const c = optMap(x => x + 1) (tOption(optTask(Some(5))));
const d = optMap(x => x + 1) (tOption(optTask(None)));
console.log(a, b, c, d);
try {
const e = optMap(x => x + 1) (tOption(delay(x => log(x)) (1000) (5)));
} catch(e) {console.log(e.message)}
是的,有很多函子没有自然变换。考虑身份仿函数。从Option
到它的转换不可能自然,因为没有什么可以将None
转换为。