Javascript 如何将TaskT与Trampoline的monad实例结合起来以获得无堆栈异步计算?

Javascript 如何将TaskT与Trampoline的monad实例结合起来以获得无堆栈异步计算?,javascript,functional-programming,monads,monad-transformers,trampolines,Javascript,Functional Programming,Monads,Monad Transformers,Trampolines,Trampoline是单子,它为单子变压器堆栈增加了堆栈安全性。它通过依赖一个特殊的解释器(monadRec)来实现这一点,解释器由一元计算的结果提供(实际上它是免费monad模式的一个专门版本)。因此,Trampolinemonad必须是最外层的monad,即transformer堆栈的基本monad 在以下设置中,TaskT(本质上是共享的Cont)是单子变换器,Trampoline是基本单子: //任务 const TaskT=TaskT=>记录( 塔克特, thisify(o=>{ o

Trampoline
是单子,它为单子变压器堆栈增加了堆栈安全性。它通过依赖一个特殊的解释器(
monadRec
)来实现这一点,解释器由一元计算的结果提供(实际上它是免费monad模式的一个专门版本)。因此,
Trampoline
monad必须是最外层的monad,即transformer堆栈的基本monad

在以下设置中,
TaskT
(本质上是共享的
Cont
)是单子变换器,
Trampoline
是基本单子:

//任务
const TaskT=TaskT=>记录(
塔克特,
thisify(o=>{
o、 taskt=k=>
taskt(x=>{
o、 taskt=k_x=>k_x;
返回k(x);
});
返回o;
}));
//单子
常量taskChainT=mmx=>fmm=>
TaskT(k=>
mmx.taskt(x=>
fmm(x)、taskt(k));
const taskOfT=x=>
TaskT(k=>k(x));
//变压器
const taskLiftT=chain=>mmx=>
TaskT(k=>chain(mmx)(k));
//辅助功能
常量taskAndT=mmx=>mmy=>
任务链T(mmx)(x=>
任务链T(mmy)(y=>
taskOfT([x,y]);
const delayTaskT=f=>ms=>x=>
TaskT(k=>setTimeout(comp(k)(f),ms,x));
常量记录=(类型,o)=>(
o[Symbol.toStringTag]=type.name | | type,o);
常数thisify=f=>f({});
常量日志=(…ss)=>
(console.log(…ss),ss[ss.length-1]);
//蹦床
常数monadRec=o=>{
而(o.tag==“链”)
o=o.fm(o.chain);
返回o.tag==“Of”
o.of
:_投掷(新类型错误(“未知蹦床标签”);
};
//标签
常量链=链=>fm=>
({tag:“Chain”,fm,Chain});
常数=Of=>
({标签:“Of”,Of});
//单子
const recOf=Of;
const recChain=mx=>fm=>
mx.tag==“链”?链(mx.Chain)(x=>recChain(mx.fm(x))(fm))
:mx.tag==“Of”?调频(mx.of)
:_投掷(新类型错误(“未知蹦床标签”);
//主要
常量foo=x=>
链(delayTaskT(x=>x)(0)(x))(Of);
常数条=taskAndT(
taskLiftT(recChain)(foo(1)))
(taskLiftT(recChain)(foo(2));//产量任务
常量main=bar.taskt(x=>Of(log(x));//生成链({fm,Chain:TaskT})

莫纳德雷克(主要);//产生[TaskT,TaskT],但[1,2]需要
此代码段中存在几个问题

问题1:没有用于
IO
的monad转换器(即
任务
) 众所周知,
IO
没有monad转换器[1]您的
TaskT
类型是根据
ContT
建模的,
ContT
确实是一个monad转换器。但是,您正在使用
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)
返回的超时id与类型
mr
不匹配。请注意,
k=>setTimeout(comp(k)(f),ms,x)
的类型应为
(b->mr)->mr

事实上,当异步调用延续
k
时,不可能变出类型为
mr
的值。
ContT
monad transformer仅适用于同步计算

然而,我们可以将
任务
定义为
Cont
的专门版本

newtype Task a=Task{Task::(a->())->()}--Task=Cont()
因此,每当
Task
出现在monad转换器堆栈中时,它总是位于底部,就像
IO
一样

如果要使
任务
monad堆栈安全,请阅读

问题2:
foo
函数的返回类型错误 让我们假设
delayTaskT
具有正确的类型。正如您已经注意到的,下一个问题是
foo
的返回类型错误


问题似乎是
foo
返回一个
TaskT
包装在
Chain
中,而这个包装的
TaskT
TaskT
链完全解耦,因此从不进行评估/激发

我假设
foo
的预期类型是
a->tasktr蹦床a
。然而,
foo
的实际类型是
a->蹦床(TaskT r m a)
。幸运的是,修复很容易

constfoo=delayTaskT(x=>x)(0);
foo
的类型与
taskOfT
相同,即
a->TaskT r m a
。我们可以专门经营蹦床

问题3:您没有正确使用
taskLiftT
taskLiftT
函数将底层的一元计算提升到
TaskT

taskLiftT::(对于所有a b.m a->(a->m b)->m b)->m a->TaskT r m a
taskLiftT(recChain)::蹦床a->TaskT r蹦床a
现在,您正在将
taskLiftT(recChain)
应用于
foo(1)
foo(2)

foo::a->蹦床(TaskT r m a)--foo的定义不正确
foo(1):蹦床(任务编号)
foo(2):蹦床(任务编号)
taskLiftT(recChain)(foo(1))::TaskT r蹦床(TaskT r m编号)
taskLiftT(recChain)(foo(2))::TaskT r蹦床(TaskT r m编号)
然而,如果我们使用了
foo
的正确定义,那么这些类型甚至不会匹配

foo::a->tasktr蹦床a——foo的正确定义
foo(1):任务r蹦床号码
foo(2):任务r蹦床号码
--无法将taskLiftT(recChain)应用于foo(1)或foo(2)