在 FFI 中使用 Aff 回调函数

Function with Aff callback in FFI

我正在尝试包装一个 Javascript 库,其中我有一个方法 A.bar(f),它将一个函数 f : B -> void.

作为参数

因为我想使用 bar 来执行一些异步计算,在 Purescript 方面,我有一个函数声明

foreign import foo :: Fn2 A (B -> Aff Unit) -> EffectFnAff Unit

在其相应的 Javascript 文件中,我有类似

的内容
exports.foo = function (a, f) {
  return function (onError, onSuccess) {
    a.bar(function (b) {
      f(b)
    })

    return function (cancelError, cancelerError, cancelerSuccess) {
      cancelerSuccess()
    }
  }
}

我遇到的问题是 f(b) 是一个 Aff 对象,我不知道如何在 Javascript 端执行它。

从 FFI-ied JavaScript 访问 PureScript 数据结构总是一个坏主意。您不仅依赖于编写库的特定方式(没有编译器支持来捕获错误!),而且还依赖于编译器本身,因为运行时表示可能会从一个编译器版本更改为另一个编译器版本(请注意,这不会适用于 EffectFnAff,因为它明确适用于 FFI 并经过仔细定义,in terms of EffectFn2)。

在 FFI 中表示有效计算的方法是通过 Effect:

foreign import foo :: Fn2 A (B -> Effect Unit) -> EffectFnAff Unit

这样的函数现在可以从 JavaScript 中以您的方式调用 - 如 f(b).

如果您希望图书馆的消费者提供 Aff,您要做的就是制作一个包装器:

foreign import foo_ :: Fn2 A (B -> Effect Unit) (EffectFnAff Unit)

foo :: A -> (B -> Aff Unit) -> Aff Unit
foo a f = fromEffectFnAff $ runFn2 foo_ a (launchAff_ <<< f)

那么您只需导出包装器 foo,而不是 FFI 导入 foo_


在某种程度上相关的说明中,我还建议取消 EffectFnAff,因为您实际上并没有启动任何异步操作,而是始终调用 cancelerSuccess().

因此,我会推荐这个:

// JavaScript
exports.foo = (a, f) => a.bar(f)

-- PureScript
foreign import foo_ :: EffectFn2 A (B -> Effect Unit) Unit

foo :: A -> (B -> Aff Unit) -> Aff Unit
foo a f = liftEffect $ runEffectFn2 foo_ a (launchAff_ <<< f)

包装器在两个地方仍然有 Aff - 这是假设您出于自己的原因需要将整个东西放入 Aff。否则它可能只是 foo = runEffectFn2 foo_