Абстрагирование композиции монады как трансформатора - PullRequest
5 голосов
/ 14 сентября 2011

Извините, если вопрос кажется немного тривиальным ... это не для меня.Я с радостью составил следующую монаду:

type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a

, которая, ну, в общем, хорошо ведет себя монада.ReaderT является преобразователем монад, а State является монадой State, а AlgRO и AlgState являются типами данных, параметризованными в i для изменяемого состояния и состояния только для чтения соответственно.Теперь, если я хочу сделать из этого аккуратный монадный трансформатор с новым типом, что-то вроде этого:

newtype SbT m i a = SbT {
    runSbT:: m ( SB i a )
}

как мне поступить?Я даже не могу собрать метод связывания (из класса типов Monad), гораздо менее «лифт» (из MonadTrans) ... Я думаю, что автоматическое получение может помочь, но я хочу понять, как это работает в этом случае.

Заранее спасибо.

Ответы [ 2 ]

10 голосов
/ 14 сентября 2011

Я не думаю, что определение для SbT - это то, что вы хотите.Это определяет состав функтора , и, предполагая, что параметр m равен Functor или Applicative, это должно сохранить эти свойства.Но такая композиция, как правило, не создает новую монаду из двух других.См. этот вопрос для получения дополнительной информации по этому вопросу.

Итак, как do вы создаете нужный монадный преобразователь, тогда?В то время как монады не сочиняют напрямую, монады трансформаторы могут быть составлены.Таким образом, чтобы построить новый трансформатор из существующих, вы просто хотите дать название этой композиции.Это отличается от newtype, который у вас есть, потому что там вы применяете m напрямую, вместо того, чтобы передавать его в стек трансформаторов.

При определении монадных трансформаторов следует помнить, что ониобязательно работать «задом наперед» определенными способами - когда вы применяете составной трансформатор к монаде, «самый внутренний» трансформатор получает первую трещину в нем, и преобразованная монада, которую он производит, - это то, с чем следующий выходной трансформатор начинает работать, & c,Обратите внимание, что это ничем не отличается от порядка, который вы получаете при применении составной функции к аргументу, например, (f . g . h) x сначала дает аргумент для h, даже если f является «первой» функцией в композиции.

Итак, ваш составной трансформатор должен взять монаду, к которой он применен, и передать ее в самый внутренний трансформатор, который, хм .... ой, получается, что SB уже уже применяется к монаде.Не удивительно, что это не сработало.Сначала нам нужно это удалить.Где это находится?Не State - мы могли бы удалить это, но мы не хотим, потому что это часть того, что вы хотите.Хм, но подождите - что опять определяется как State?Ах да:

type State s = StateT s Identity

Ага, поехали.Давайте вытащим это Identity оттуда.Мы переходим от вашего текущего определения:

type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a

к эквивалентной форме:

type SB i a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) Identity ) a

Затем выгоняем ленивую задницу:

type SB' i m a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a
type SB i a = SB' i Identity a

Но сейчас SB' выглядит подозрительно, как определение монадного преобразователя, и не без оснований, потому что это так.Итак, мы воссоздаем оболочку newtype и отбрасываем несколько экземпляров:

newtype SbT i m a = SbT { getSB :: ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a }

instance (Functor m) => Functor (SbT i m) where
    fmap f (SbT sb) = SbT (fmap f sb)

instance (Monad m) => Monad (SbT i m) where
    return x = SbT (return x)
    SbT m >>= k = SbT (m >>= (getSB . k))

instance MonadTrans (SbT i) where
    lift = SbT . lift . lift

runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t)
runSbT (SbT m) e s = runStateT (runReaderT m e) s

Несколько вещей, на которые следует обратить внимание: функция runSbT здесь не является полевым средством доступа, а скорее составнойФункция «запустить» для каждого трансформатора в стеке, о котором мы знаем.Точно так же функция lift должна поднять один раз для двух внутренних трансформаторов, затем добавить заключительную обертку newtype.Из-за этого они работают как один монадный преобразователь, скрывая тот факт, что это на самом деле составной.

Если вы хотите, то было бы просто написать экземпляры для MonadReader и MonadState., подняв экземпляры для составных трансформаторов.

2 голосов
/ 14 сентября 2011

Вы намеревались обернуть дополнительные m вокруг вещей в вашем новом типе? Я бы предложил следующее:

newtype Sb i a = Sb { runSb :: SB i a }

... что должно немного облегчить написание вашего instance Monad (Sb i). Если вы действительно пытаетесь написать монадный преобразователь, то вам следует использовать преобразователи до конца; например,

type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a
newtype SbT m i a = SbT { runSbT :: SBT m i a }

В качестве второй точки интереса часто предпочтительнее η-уменьшить type синонимы (поскольку они всегда должны быть "полностью применены"); выполнение этого с SB и SBT будет выглядеть так:

type SB i = ReaderT (AlgRO i) (State (AlgState i))
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m)
...