2010-04-18 9 views
3

Я пытаюсь создать трансформатор монады для будущего проекта, но, к сожалению, моя реализация Monad typeclasse (>> =) не работает.Ошибка типа при попытке реализовать функцию (>> =) для создания настраиваемого монадного трансформатора

Прежде всего, здесь является реализация Базового монады:

newtype Runtime a = R { 
    unR :: State EInfo a 
} deriving (Monad) 

Здесь реализация typeclasse Монада осуществляется автоматически GHC (с использованием GeneralizedNewtypeDeriving языка прагму). Монада трансформатора определяется как так:

newtype RuntimeT m a = RuntimeT { 
    runRuntimeT :: m (Runtime a) 
} 

Проблема возникает от того, как я функции создания экземпляра (>> =) в typeclasse Монады:

instance (Monad m) => Monad (RuntimeT m) where 
    return a = RuntimeT $ (return . return) a 
    x >>= f = runRuntimeT x >>= id >>= f 

так, как я это вижу, первая >>= работает в лежащей в основе m монада. Таким образом, runRuntimeT x >>= возвращает значение типа Runtime a (справа?). Затем, следующий код, id >>=, должен вернуть значение типа a. Это значение передается функции f типа f :: (Monad m) => a -> RuntimeT m b.

И вот возникает проблема типа: тип функции f не соответствует типу, требуемому функцией (>> =). Джоу я могу сделать это последовательным? Я понимаю, почему это не работает, но я не могу превратить его в нечто функциональное.

Edit: Сообщение об ошибке:

Core.hs:34:4: 
    Occurs check: cannot construct the infinite type: m = RuntimeT m 
    When generalising the type(s) for `>>=' 
    In the instance declaration for `Monad (RuntimeT m)' 
Failed, modules loaded: none. 

Спасибо вам помочь, и не стесняйтесь, чтобы исправить любые недостатки в моем сообщении,
Чарли П.

+1

Пожалуйста, покажите нам сообщение об ошибке, которое вы получаете. – dave4420

ответ

3

Чтобы узнать, что сказал Рейд Бартон о StateT - вот как вы бы определили RuntimeT, используя StateT. Мода Runtime может быть тривиально определена с использованием тождественной монады.

newtype RuntimeT m a = R { 
    unR :: StateT EInfo m a 
} 

type Runtime = RuntimeT Identity 

instance Monad m => Monad (RuntimeT m) where 
    return = R . return 

    (R m) >>= f = R (m >>= unR . f) 
+0

Я понятия не имел, что существует монада Идентичности, и похоже, что это то, к чему я пытаюсь добраться. Однако, как я уже говорил, комментируя сообщение Рейда Бартона, идея создания этого нового монада и монада-трансформатора заключается в том, чтобы скрыть государственную монаду от пользователя. Как я могу достичь этого, используя то, что вы указали в своем сообщении? – CharlieP

+2

@CharlieP: Проблема заключается в том, что StateT сэндвичит основную монаду между s -> и (a, s), в то время как ваш попытка трансформатора не смогла этого сделать. Если вы предпочитаете, вы можете выбрать вывод Monad (или даже MonadState EInfo) для нового типа, который также предоставил Alasdair, но без использования StateT или ручного кодирования его эквивалента вы не сможете использовать свою монаду в качестве трансформатора. Расширение определения StateT дает newtype RuntimeT ma = RuntimeT {unRuntimeT :: EInfo -> m (a, EInfo)}, но ваше определение дает newtype RuntimeT ma = RuntimeT {unRuntimeT :: m (EInfo -> (a, EInfo))} –

+2

Charlie, вы обычно используете модульную систему Haskell, чтобы скрыть детали реализации 'RuntimeT'. Если вы не экспортируете 'unR' или' R', пользователю никогда не нужно знать, что вы использовали 'StateT'. Например, вам может понадобиться эквивалент 'runStateT' для' RuntimeT' - который может быть реализован как 'runRuntimeT = runStateT. unR'. Обратите внимание, что это тип 'RuntimeT m a -> EInfo -> m (a, EInfo)'. 'StateT' не появляется нигде, и кто-то, использующий эту функцию, может использовать его, будучи блаженно не осведомленным о существовании StateT. Аналогичные функции могут быть записаны для get/put. Надеюсь, это поможет. – Alasdair

4

Обычная StateT s m монада посылает a to s -> m (a, s), но вы работаете с m (s -> (a, s)). Я не думаю, что последний образует монаду для общего s. Вы уверены, что не хотите использовать StateT?


Вот почему я не думаю, что am (s -> (a, s)) монада: Для того, чтобы написать >>= мне нужна функция, которая принимает аргументы типов

m (s -> (a, s)) 
a -> m (s -> (b, s)) 

и возвращает значение типа

m (s -> (b, s)) 

«Эффекты» (т. Е. fmap (const())) результата должны быть таковыми из первого аргумента, так как у нас нет способа g et a a, чтобы перейти ко второму аргументу. Так как m появляется только снаружи типа результата, мы не можем использовать второй аргумент для чего-либо вообще — не будет возможности избавиться от m, который он вводит.

+0

Представьте, что у меня есть функция, работающая внутри монады Runtime. Эта монада абсолютно непрозрачна, и пользователь не должен знать, что это на самом деле государственная монада с сахаром сверху. Идея трансформатора монады RuntimeT заключается в возможности использовать функции, выполняемые в монаде Runtime прозрачно с, скажем, функциями, запущенными в монаде IO. Вот почему я не могу использовать трансформер монады StateT вместо моего обычного. – CharlieP

+1

Спасибо, что указали на нелогичную часть моей монады, я собираюсь использовать подход, который вы и Аласдейр заявили в сочетании с монадой-личностью. – CharlieP