У меня есть 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
I знаю, почему это не работает. Что меня смущает, так это то, что они, по-видимому, вообще не являются естественным преобразованием, как только 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)}