0

Let's first look at a similar function from the web-gl package, for which the intention works:

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)

This basically turns the callback based withShaders function into one that can be used in aff context.

I'm wondering why, the same thing does not work with the following function (also from the webgl package):

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)

This gives me an 'infinite type error' for the last function. I guess it's because here the error callback and the success callback don't share the same result type, but I could not find away to fix this.


EDIT

After reading the accpeted answer, the solution is:

runWebGLAff arg = makeAff (\err ok -> runWebGL arg (error >>> err) (unsafeCoerceEff <<< ok))
Micha
  • 53
  • 5

1 Answers1

0

EffWebGL is defined as follows in the library:

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

It's just an alias for Eff, but notice that its effect row includes the WebGl effect.

When the compiler tries to reconcile this in your function, it deduces, from the usage of ok as callback, that ok :: Eff (webgl | eff) a, but since the ok callback must have the same type as the error callback (from the signature of makeAff), the compiler also deduces that err :: Eff (webgl | eff) a, and therefore, error >>> err :: Eff (webgl | eff) a. However, since error >>> err is used as parameter to runWebGL, it means that error >>> err :: Eff eff a (that's how runWebGL is defined). So the compiler now has two pieces of information that must be true:

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

And this, of course, must mean that (webgl | eff) === eff. So eff is part of its own definition. Aka "infinite type". That's why you're getting the error.

Now, in order to fix it, you must take your parameter err as Eff (webgl | eff) a (as required by the type of makeAff), but pass it to runWebGL as Eff eff a (as required by the type of runWebGL) - i.e. the effect row would widen as it flows from the inner callback to the outer. This should be perfectly safe to do, since the inner callback would never use this effect anyway (as indicated by its type), however, the compiler doesn't have a safe way to do this - which might be one of the reasons the effect rows were ditched in PureScript 0.12 (and good riddance!)

Instead, you have to use the type-unsafe way from Control.Monad.Eff.Unsafe:

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

Just wrap the error callback in a call to this function, and it will work:

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

P.S. Normally I would recommend switching to PS 0.12, but it seems you can't do that, because the WebGL library hasn't been updated (yet?).

Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • Great, thanks for the detailed information! It turned out that the compiler is only happy if the ok-callback is wrapped with `unsafeCoerceEff` instead of the error one. I'll put the final working example to the post above.. Regarding PS.12, yes - the web-gl package is not updated yet and so I have to use an older PS version + tons of old, old libraries (e.g. `aff` in 3... something) – Micha Sep 14 '18 at 15:50
  • Don't coerce the ok callback, that actually might be dangerous, because the callback is using the webgl effect, but you're hiding it from the consumer. – Fyodor Soikin Sep 14 '18 at 17:32