您可能需要考虑使用包中的 continuation monad 转换ContT
器purescript-transformers
。
ContT
该包中的定义为
newtype ContT r m a = ContT ((a -> m r) -> m r)
如果你设置r
为 beUnit
和m
to be Eff eff
,你会得到一些看起来有点像asyncGetNumber
构造函数内部的类型的东西:
(a -> Eff eff Unit) -> Eff eff Unit
现在,确保eff
包含您需要的效果,您应该能够包装您的
asyncGetNumber
函数以用于ContT
:
asyncGetNumberCont :: ContT Unit (Eff SomeEffects) Number
asyncGetNumberCont = ContT $ \callback ->
asyncGetNumber "/numberLocation" callback
要不就
asyncGetNumberCont = ContT $ asyncGetNumber "/numberLocation"
请注意,参数ContT
将回调作为参数。
您现在可以使用 do 表示法串联异步计算:
do n <- asyncGetNumberCont
m <- asyncGetNumberCont
return (n + m)
您甚至可以创建一个应用函子,它包装ContT
并支持异步计算的并行组合。您可能还对purescript-node-thunk
提供这种开箱即用功能的包感兴趣。
编辑:另一种选择是ContT
使用不同于Eff
. 您可以按如下方式执行此操作:
-- Ignore effects to keep things simple
-- The runtime representation of 'Async a' is a function which takes a callback,
-- performs some side effects an returns.
foreign import data Async :: * -> *
-- Make an async computation from a function taking a callback
foreign import makeAsync
"function makeAsync(f) {\
\ return function(k) {\
\ f(function(a) {\
\ return function() {\
\ k(a)();\
\ };\
\ })();\
\ };\
\}" :: forall a eff. ((a -> Eff eff Unit) -> Eff eff Unit) -> Async a
-- Now we need to define instances for Async, which we can do using FFI
-- calls, for example:
foreign import fmapAsync
"function fmapAsync(f) {\
\ return function (comp) {\
\ return function (k) {\
\ comp(function(a) {\
\ k(f(a));\
\ });
\ };\
\ };\
\}" :: forall a b. (a -> b) -> Async a -> Async b
instance functorAsync :: Functor Async where
(<$>) = fmapAsync
等等。这很快就会变得很麻烦,因为您实际上是在重复ContT
.
此外,如果没有编译器中的重写规则支持,就无法像使用Eff
.