关联一个在错误和成功回调中具有不同结果类型的函数

时间:2018-09-14 09:39:39

标签: purescript

让我们首先看一下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)

这给我最后一个函数的“无限类型错误”。我猜是因为错误回调和成功回调没有共享相同的结果类型,但是我找不到解决的办法。


编辑

阅读了正确的答案后,解决方法是:

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

1 个答案:

答案 0 :(得分:0)

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是其自身定义的一部分。又名“无限类型”。这就是为什么您会收到错误消息。

现在,为了修复它,您必须将参数err设为Eff (webgl | eff) a(根据makeAff类型的要求),然后将其传递给runWebGLEff 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库尚未更新(还好吗?)。