Как справиться с побочными эффектами с помощью Eff Monad

Я немного озадачен этим сообщением об ошибке

Warning: Error in module Chapter2:
Error in value declaration unionIsForeign:
Error checking that type
  Control.Monad.Eff.Eff (trace :: Debug.Trace.Trace | u8717) u8715
subsumes type
  Data.Either.Either Data.Foreign.ForeignError Chapter2.Union
Error at src/Chapter2.purs line 16, column 18 - line 20, column 1:
  Cannot unify type
    Control.Monad.Eff.Eff
  with type
    Data.Either.Either

это результат этого кода

module Chapter2 where

import Debug.Trace
import Data.Foreign
import Data.Foreign.Class
import Control.Monad.Eff

sideeffect :: forall e. Number -> Eff (trace :: Trace | e) Number
sideeffect v = return v

data Union = S String 

instance unionIsForeign :: IsForeign Union where
    read value = do
        v <- sideeffect 42
        return $ S "Test"

main = do
    trace "Test"

Зачем нужно унифицировать Eff и Either? Похоже, это имеет место только при создании экземпляра класса типа IsForeign. sideeffectуспешно работает в main- конечно, main имеет сигнатуру, указывающую на использование Eff Monad

> :t Chapter2.main
Compiling Chapter2
forall t20. Control.Monad.Eff.Eff (trace :: Debug.Trace.Trace | t20) Prelude.Unit

person robkuz    schedule 25.05.2015    source источник


Ответы (1)


Ключевые части:

с 1_:

class IsForeign a where
  read :: Foreign -> F a

и от Data.Foreign:

type F = Either ForeignError

Итак, ваша функция read имеет объявленный возвращаемый тип Either ForeignError Union.

Но поскольку вы используете sideeffect, тип возвращаемого значения которого равен Eff (trace :: Trace | e) Number, в блоке do предполагаемым типом блока do является значение Eff ....

То есть вы пытаетесь вернуть Eff ... из чего-то, что должно вернуть Either ..., а компилятор жалуется, что не может объединить Eff ... с Either ....

person Brenton Alker    schedule 26.05.2015
comment
Что ж, да, это правильно, но это не то, чего я хочу. Я не хочу, чтобы блок do определял тип как Eff ... , поскольку я возвращаю return $ S "Test". Итак, как я могу сказать компилятору не делать этого? Или еще лучше, как я могу вызвать побочный эффект, не меняя подпись read - person robkuz; 26.05.2015
comment
Вы можете использовать Control.Monad.Eff.runPure <<< Control.Monad.Eff.Unsafe.unsafeInterleaveEff, функцию типа forall eff a. Eff eff a -> a, если вам действительно нужно выполнять действия Eff в таких местах. Но я бы этого не одобрял — это подрыв системы типов. Вся причина, по которой существует система типов, состоит в том, чтобы дать вам надежные гарантии того, чего определенные части вашей программы не делают, и, разрушая таким образом систему типов, вы ослабляете эти гарантии. - person hdgarrood; 26.05.2015
comment
@hdgarrood: вздох Я знаю, но тогда. Как просто добавить операторы трассировки в мою программу, не меняя подписи во всей цепочке вызовов. Это была моя первоначальная проблема. Если вы замените v <- sideeffect 42 в приведенном выше коде просто на trace "Hello", у вас будет та же проблема (очевидно, поскольку трассировка является побочным эффектом). Но трассировка так распространена... - person robkuz; 26.05.2015
comment
Если это просто для целей отладки/понимания, просто используйте unsafe. Но если он предназначен для постоянного использования, то да, вам нужно будет изменить необходимые подписи (но все же, возможно, не на Eff, Writer или что-то подобное, может быть уместно в зависимости от намерения.) - person Brenton Alker; 27.05.2015
comment
достаточно справедливо - тогда да, что сказал @Brenton. Кстати, я не очень часто хочу делать это — обычно я использую REPL для проверки подобных вещей. - person hdgarrood; 27.05.2015