2015-05-25 8 views
1

Я немного озадачен этим сообщением об ошибкеКак справиться с побочными эффектами с Eff Монады

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 - конечно главная имеет подпись, которая гласит его использование в Eff Монада

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

ответ

1

Основные части:

из Data.Foreign.Class:

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 ....

+0

Хорошо, да, это правильно - но это не то, что я хочу. Я не хочу, чтобы блок 'do' выводил тип' Eff ... ', поскольку я возвращаю' return $ S "Test" '. Итак, как я могу сказать компилятору не делать этого? Или даже лучше, как я могу назвать побочный эффект без изменения подписи 'read' – robkuz

+1

Вы можете использовать' Control.Monad.Eff.runPure <<< Control.Monad.Eff.Unsafe.unsafeInterleaveEff', который является функцией типа 'forall eff a. Eff eff a -> a', если вам действительно нужно выполнять действия Eff в таких местах. Но я бы обескураживал это - это подрывная система типа. Вся причина, по которой существует система типов, заключается в том, чтобы дать вам надежные гарантии того, что некоторые части вашей программы не выполняют, и таким образом, подрывая систему типов, вы ослабляете эти гарантии. – hdgarrood

+0

@hdgarrood: * вздох * Я знаю - но тогда. Как просто добавить инструкции трассировки в свою программу, не меняя подписи во всей моей цепочке вызовов. Это была моя первоначальная проблема. Если вы замените 'v <- sideeffect 42' в моем вышеприведенном коде просто« trace »Hello», у вас будет такая же проблема (очевидно, так как трассировка является побочным эффектом). Но трассировка настолько распространена ... – robkuz