此代码段中有几个问题。
IO
问题 #1: (ie Task
)没有 monad 转换器
众所周知,没有用于IO
. [1]你的TaskT
类型是仿照的ContT
,ContT
确实是一个单子转换器。但是,您正在使用TaskT
执行异步计算,例如setTimeout
,这就是问题出现的地方。
考虑 的定义TaskT
,它类似于ContT
。
newtype TaskT r m a = TaskT { taskt :: (a -> m r) -> m r }
因此,delayTaskT
应该有类型(a -> b) -> Number -> a -> TaskT r m b
。
const delayTaskT = f => ms => x =>
TaskT(k => setTimeout(comp(k) (f), ms, x));
但是,setTimeout(comp(k) (f), ms, x)
返回一个与 type 不匹配的超时 id m r
。注意k => setTimeout(comp(k) (f), ms, x)
应该有类型(b -> m r) -> m r
。
事实上,当异步调用m r
延续时,不可能变出一个类型的值。k
monad 转换器ContT
仅适用于同步计算。
不过,我们可以Task
将Cont
.
newtype Task a = Task { task :: (a -> ()) -> () } -- Task = Cont ()
因此,无论何时Task
存在于 monad 转换器堆栈中,它都将始终位于底部,就像IO
.
如果您想让Task
monad 堆栈安全,请阅读以下答案。
问题 #2:foo
函数的返回类型错误
让我们暂时假设delayTaskT
具有正确的类型。正如您已经注意到的,下一个问题是foo
返回类型错误。
问题似乎是foo
哪个返回 aTaskT
包裹在 a 中Chain
,而这个包裹TaskT
与链完全分离,TaskT
因此永远不会被评估/解雇。
我假设预期的类型foo
是a -> TaskT r Trampoline a
. 但是,实际类型foo
是a -> Trampoline (TaskT r m a)
. 幸运的是,修复很容易。
const foo = delayTaskT(x => x) (0);
的类型foo
与 相同taskOfT
,即a -> TaskT r m a
。我们可以专攻m = Trampoline
。
问题#3:你没有taskLiftT
正确使用
该taskLiftT
函数将底层的一元计算提升到TaskT
层中。
taskLiftT :: (forall a b. m a -> (a -> m b) -> m b) -> m a -> TaskT r m a
taskLiftT(recChain) :: Trampoline a -> TaskT r Trampoline a
现在,您正在taskLiftT(recChain)
申请foo(1)
和foo(2)
。
foo :: a -> Trampoline (TaskT r m a) -- incorrect definition of foo
foo(1) :: Trampoline (TaskT r m Number)
foo(2) :: Trampoline (TaskT r m Number)
taskLiftT(recChain) (foo(1)) :: TaskT r Trampoline (TaskT r m Number)
taskLiftT(recChain) (foo(2)) :: TaskT r Trampoline (TaskT r m Number)
但是,如果我们使用正确的定义,foo
那么类型甚至不会匹配。
foo :: a -> TaskT r Trampoline a -- correct definition of foo
foo(1) :: TaskT r Trampoline Number
foo(2) :: TaskT r Trampoline Number
-- Can't apply taskLiftT(recChain) to foo(1) or foo(2)
如果我们使用正确的定义,foo
那么有两种方法来定义bar
. 请注意,无法正确定义foo
using setTimeout
。因此,我重新定义foo
为taskOfT
.
使用foo
和不使用taskLiftT
.
const bar = taskAndT(foo(1))(foo(2)); // yields TaskT
// TASK
const TaskT = taskt => record(
TaskT,
thisify(o => {
o.taskt = k =>
taskt(x => {
o.taskt = k_ => k_(x);
return k(x);
});
return o;
}));
// Monad
const taskChainT = mmx => fmm =>
TaskT(k =>
mmx.taskt(x =>
fmm(x).taskt(k)));
const taskOfT = x =>
TaskT(k => k(x));
// Transformer
const taskLiftT = chain => mmx =>
TaskT(k => chain(mmx) (k));
// auxiliary functions
const taskAndT = mmx => mmy =>
taskChainT(mmx) (x =>
taskChainT(mmy) (y =>
taskOfT([x, y])));
const delayTaskT = f => ms => x =>
TaskT(k => setTimeout(comp(k) (f), ms, x));
const record = (type, o) => (
o[Symbol.toStringTag] = type.name || type, o);
const thisify = f => f({});
const log = (...ss) =>
(console.log(...ss), ss[ss.length - 1]);
// TRAMPOLINE
const monadRec = o => {
while (o.tag === "Chain")
o = o.fm(o.chain);
return o.tag === "Of"
? o.of
: _throw(new TypeError("unknown trampoline tag"));
};
// tags
const Chain = chain => fm =>
({tag: "Chain", fm, chain});
const Of = of =>
({tag: "Of", of});
// Monad
const recOf = Of;
const recChain = mx => fm =>
mx.tag === "Chain" ? Chain(mx.chain) (x => recChain(mx.fm(x)) (fm))
: mx.tag === "Of" ? fm(mx.of)
: _throw(new TypeError("unknown trampoline tag"));
// MAIN
const foo = taskOfT;
const bar = taskAndT(foo(1))(foo(2)); // yields TaskT
const main = bar.taskt(x => Of(log(x))); // yields Chain({fm, chain: TaskT})
monadRec(main); // yields [TaskT, TaskT] but [1, 2] desired
不要使用foo
和使用taskLiftT
.
const bar = taskAndT(
taskLiftT(recChain) (Of(1)))
(taskLiftT(recChain) (Of(2))); // yields TaskT
// TASK
const TaskT = taskt => record(
TaskT,
thisify(o => {
o.taskt = k =>
taskt(x => {
o.taskt = k_ => k_(x);
return k(x);
});
return o;
}));
// Monad
const taskChainT = mmx => fmm =>
TaskT(k =>
mmx.taskt(x =>
fmm(x).taskt(k)));
const taskOfT = x =>
TaskT(k => k(x));
// Transformer
const taskLiftT = chain => mmx =>
TaskT(k => chain(mmx) (k));
// auxiliary functions
const taskAndT = mmx => mmy =>
taskChainT(mmx) (x =>
taskChainT(mmy) (y =>
taskOfT([x, y])));
const delayTaskT = f => ms => x =>
TaskT(k => setTimeout(comp(k) (f), ms, x));
const record = (type, o) => (
o[Symbol.toStringTag] = type.name || type, o);
const thisify = f => f({});
const log = (...ss) =>
(console.log(...ss), ss[ss.length - 1]);
// TRAMPOLINE
const monadRec = o => {
while (o.tag === "Chain")
o = o.fm(o.chain);
return o.tag === "Of"
? o.of
: _throw(new TypeError("unknown trampoline tag"));
};
// tags
const Chain = chain => fm =>
({tag: "Chain", fm, chain});
const Of = of =>
({tag: "Of", of});
// Monad
const recOf = Of;
const recChain = mx => fm =>
mx.tag === "Chain" ? Chain(mx.chain) (x => recChain(mx.fm(x)) (fm))
: mx.tag === "Of" ? fm(mx.of)
: _throw(new TypeError("unknown trampoline tag"));
// MAIN
const foo = taskOfT;
const bar = taskAndT(
taskLiftT(recChain) (Of(1)))
(taskLiftT(recChain) (Of(2))); // yields TaskT
const main = bar.taskt(x => Of(log(x))); // yields Chain({fm, chain: TaskT})
monadRec(main); // yields [TaskT, TaskT] but [1, 2] desired
[1] 为什么Haskell中没有IO转换器?