aff-ify функция, которая имеет различные результирующие типы ошибок и успешных обратных вызовов - PullRequest
0 голосов
/ 14 сентября 2018

Давайте сначала посмотрим на аналогичную функцию из пакета 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 context.

Мне интересно, почему то же самое не работает со следующей функцией (также из пакета 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 голосов
/ 14 сентября 2018

EffWebGL определяется следующим образом в библиотеке :

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 (и это хорошо!)

Вместо этого вы должны использовать небезопасный способ из 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 Обычно я бы порекомендовал перейти на PS 0.12, но, похоже, вы не можете этого сделать, потому чтоБиблиотека WebGL не была обновлена ​​(пока?).

...