aff-ify 在错误和成功回调中具有不同结果类型的函数

aff-ify a function that has different resulting types in error and success callback

让我们先看看 web-gl 包中的一个类似函数,其意图有效:

withShaders
  :: forall bindings eff a
   . Shaders ({ | bindings }) -> (String -> EffWebGL eff a) -> ({ webGLProgram :: WebGLProg | bindings } -> EffWebGL eff a) -> EffWebGL eff a


makeAff
  :: forall e a
   . ((Error -> Eff e Unit) -> (a -> Eff e Unit) -> Eff e Unit) -> Aff e a


withShadersAff :: forall eff a. Shaders { | a } -> Aff ( webgl ∷ WebGl | eff ) { webGLProgram ∷ WebGLProg | a }
withShadersAff arg = makeAff (\err ok -> withShaders arg (error >>> err) ok)

这基本上将基于回调的 withShaders 函数变成了可以在 aff 上下文中使用的函数。

我想知道为什么,同样的事情不适用于以下函数(也来自 webgl 包):

runWebGL :: forall a eff. String -> (String -> Eff eff a) -> (WebGLContext -> EffWebGL eff a) -> Eff eff a


runWebGLAff :: forall eff . String -> Aff ( webgl ∷ WebGl | eff ) WebGLContext
runWebGLAff arg = makeAff (\err ok -> runWebGL arg (error >>> err) ok)

这为我提供了最后一个函数的 'infinite type error'。我猜是因为这里的错误回调和成功回调不共享相同的结果类型,但我找不到办法解决这个问题。


编辑

看完接受的答案后,解决方案是:

runWebGLAff arg = makeAff (\err ok -> runWebGL arg (error >>> err) (unsafeCoerceEff <<< ok))

EffWebGL定义如下in the library:

type EffWebGL eff a = Eff (webgl :: WebGl | eff) a

它只是 Eff 的别名,但请注意它的效果行包括 WebGl 效果。

当编译器试图在你的函数中协调它时,它从 ok 作为回调的用法推断出 ok :: Eff (webgl | eff) a,但由于 ok 回调必须具有与错误回调的类型相同(来自 makeAff 的签名),编译器也推断出 err :: Eff (webgl | eff) a,因此,error >>> err :: Eff (webgl | eff) a。但是,由于 error >>> err 用作 runWebGL 的参数,这意味着 error >>> err :: Eff eff a(这就是 runWebGL 的定义方式)。所以编译器现在有两条信息必须为真:

(1)  error >>> err :: Eff (webgl | eff) a
(2)  error >>> err :: Eff eff a

而这当然是指(webgl | eff) === eff。所以 eff 是它自己定义的一部分。又名 "infinite type"。这就是您收到错误的原因。

现在,为了修复它,您必须将参数 err 作为 Eff (webgl | eff) a(根据 makeAff 类型的要求),但将其传递给 runWebGL as Eff eff a(根据 runWebGL 类型的要求)——即当效果行从内部回调流向外部时,它会变宽。这应该是绝对安全的,因为内部回调永远不会使用这个效果(如其类型所示),但是,编译器没有安全的方法来做这个 - 这可能是效果的原因之一行在 PureScript 0.12 中被抛弃了(很好的摆脱!)

相反,您必须使用类型不安全的方式 from Control.Monad.Eff.Unsafe:

unsafeCoerceEff :: forall eff1 eff2 a. Eff eff1 a -> Eff eff2 a

只需将错误回调包装在对此函数的调用中,它就会起作用:

runWebGLAff arg = makeAff (\err ok -> runWebGL arg (unsafeCoerceEff $ error >>> err) ok)

P.S。通常我会建议切换到 PS 0.12,但似乎你不能那样做,因为 WebGL 库还没有更新(还没有?)。