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)