让我们首先看一下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))
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
的类型的要求),但是将它作为runWebGL
传递给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)
附:通常我会建议切换到PS 0.12,但似乎你不能这样做,因为WebGL库还没有更新(但是?)。