现在,我有一个异步函数,其工作原理如下:
foo = do
ayncGetNumber "/numberLocation" \a ->
(trace <<< show) a
但是这种回调样式(根据我的理解)是不可组合的,我希望它像这样工作
foo = do
a <- ayncGetNumber "/numberLocation"
(trace <<< show) a
或者
foo = ayncGetNumber "/numberLocation" >>= show >>> trace
但是我不知道如何从回调中逃脱并使其可组合。
最佳答案
您可能要考虑使用ContT
包中的延续monad转换器purescript-transformers
。
该软件包中ContT
的定义为
newtype ContT r m a = ContT ((a -> m r) -> m r)
如果将
r
设置为Unit
,并将m
设置为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
那样获得内联绑定(bind)。