Функция с обратным вызовом Aff в FFI - PullRequest
1 голос
/ 08 апреля 2020

Я пытаюсь обернуть библиотеку 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.

1 Ответ

3 голосов
/ 08 апреля 2020

Доступ к структурам данных PureScript из FFI-ied JavaScript всегда плохая идея. Вы полагаетесь не только на указанный c способ написания библиотеки (без поддержки компилятора для обнаружения ошибок!), Но и на сам компилятор, поскольку представление во время выполнения может меняться от одной версии компилятора к другой (обратите внимание, что это не относится к EffectFnAff, потому что он явно предназначен для FFI и тщательно определен как таковой, в терминах 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_

...