2014-11-07 2 views
2

Я не понимаю, почему этот код typechecks:Неявное принуждение типа?

error1 :: ErrorT String (ReaderT Int IO) Int 
error1 = asks id 

FYI, то asks имеет этот тип:

asks :: Monad m => (r -> a) -> ReaderT r m a 

С другой стороны, я могу понять, что этот код typechecks :

reader1 :: ReaderT Int IO Int 
reader1 = asks id 

id имеет тип a -> a и есть экземпляр Monad для IO, поэтому компилятор может вывести тип. Это понятно для меня.

ErrorT является NewType и haskell spec состоянием, (в разделе о ньютайпах):

... это создает особый тип, который должен быть явно принуждением или от исходного типа ...

по моей интерпретации, я должен быть в состоянии получить тот же тип, как и в error1только явно, с каким-то принуждением похожее на это:

reader2 :: ReaderT Int IO (Either String Int) 
reader2 = fmap (\i -> Right i) reader1 

error2 :: ErrorT String (ReaderT Int IO) Int 
error2 = ErrorT reader2 

Но, судя по всему, с тех пор, как error1 typechecks просто отлично, у меня есть некоторые знания. Можете ли вы помочь раскрыть его для меня?

импортирует необходимую для запуска примера кода:

import Control.Monad.Error (ErrorT(ErrorT)) 
import Control.Monad.Reader (ReaderT, asks) 
+3

Тип, который вы указываете для 'asks', является типом из' Control.Monad.Trans.Reader'. Однако в 'Control.Monad.Reader'' asks' имеет более общий тип, а именно 'запрашивает :: MonadReader r m => (r -> a) -> m a'. – kosmikus

+0

Если вы используете последнюю версию 'mtl', то' Control.Monad.Reader.asks' имеет тип 'MonadReader r m => (r -> a) -> m a)'. – bheklilr

ответ

9

Функция asks экспортируется два модулей, связанных с несколькими различными типами. Версия от Control.Monad.Trans.Reader (части transformers пакета), имеет вид, приведенный в вопросе:

asks :: Monad m => (r -> a) -> ReaderT r m a 

Однако версия используется, кажется, один в mtl упаковки от Control.Monad.Reader модуля, который имеет следующий, более общий тип:

asks :: MonadReader r m => (r -> a) -> m a 

Так пример определения

error1 :: ErrorT String (ReaderT Int IO) Int 
error1 = asks id 

означает, что

MonadReader Int (ErrorT String (ReaderT Int IO)) 

должно быть выполнено.

Также определяется mtl являются следующие случаи для MonadReader:

instance Monad m => MonadReader r (ReaderT r m) 
instance (Error e, MonadReader r m) => MonadReader r (ErrorT e m) 

С этим, ограничение выше сводится к

(Error String, Monad IO) 

которые оба держать так же.

0

Я думаю, что часть вашего ответа заключается в том, что монадические функции как asks, put, get, throwError и т.д. в mtl упаковке записываются автоматически возвыситься в зависимости от того, как оценивается стек монады.

Например, следующая функция:

foo = do a <- asks id 
     if a < 0 then throwError "oops" 
        else return $ sqrt a 

может иметь оба типа:

  • ErrorT String (ReaderT Двойной м) Двойной
  • ReaderT (ErrorT Струнный м Двухместный) Двухместный

в зависимости от порядка, в котором runReaderT и runErrorT.

Наиболее общий тип этой функции:

foo :: (MonadError [Char] m, MonadReader b m, Ord b, Floating b) => m b 

, который показывает, что нет никакого априорного упорядочения к монадным слоям.

В вашем примере вы указали подпись типа, в которой говорится, что в вашей монаде есть слой ErrorT, хотя вы не использовали функцию throwError. Это просто эквивалентно добавлению ограничения MonadError [Char] m к сигнатуре типа.

Смежные вопросы