2013-05-14 10 views
7

В настоящее время я испытываю трудности с трансформаторами монады. Я определяю несколько разных недетерминированных отношений, которые используют трансформаторы. К сожалению, у меня возникли проблемы с пониманием того, как правильно переводить из одной эффективной модели в другую.Трансформация под трансформаторами

Предположим, что эти отношения являются «foo» и «bar». Предположим, что «foo» связывает As и Bs с Cs; предположим, что «бар» связывает Bs и Cs с Ds. Мы определим «бар» в терминах «foo». Чтобы сделать интереснее, вычисление этих отношений потерпит неудачу по-разному. (Поскольку бар соотношение зависит от Foo отношения, его случаи отказа являются надстройкой.), Поэтому я даю следующие определения типа:

data FooFailure = FooFailure String 
data BarFailure = BarSpecificFailure | BarFooFailure FooFailure 
type FooM = ListT (EitherT FooFailure (Reader Context)) 
type BarM = ListT (EitherT BarFailure (Reader Context)) 

Я бы тогда ожидать, чтобы иметь возможность писать отношения со следующими подписями функций :

foo :: A -> B -> FooM C 
bar :: B -> C -> BarM D 

Моя проблема заключается в том, что при написании определения «бара», мне нужно, чтобы иметь возможность получать сообщения об ошибках от «Foo» связи и правильно представлять их в пространстве «бар». Так что я бы хорошо с функцией вида

convert :: (e -> e') -> ListT (EitherT e (Reader Context) a 
        -> ListT (EitherT e' (Reader Context) a 

Я могу даже написать, что зверек, запустив ListT, отображение на EitherT, а затем перекомпоновки ListT (потому что это происходит, что т [а] может преобразуется в ListT ma). Но это кажется ... грязным.

Существует веская причина, по которой я не могу просто запустить трансформатор, сделать некоторые вещи под ним и в целом «вернуть его»; трансформатор, который я запускал, может иметь эффекты, и я не могу магически их уничтожить. Но есть ли способ, по которому я могу поднять функцию достаточно далеко в стеке трансформатора, чтобы выполнить какую-то работу для меня, поэтому мне не нужно писать функцию convert, показанную выше?

ответ

3

Я думаю, что новообращенное хороший ответ, и используя Control.Monad.Morph и Control.Monad.Trans.Either это (почти) на самом деле просто написать:

convert :: (Monad m, Functor m, MFunctor t) 
      => (e -> e') 
      -> t (EitherT e m) b -> t (EitherT e' m) b 
convert f = hoist (bimapEitherT f id) 

небольшая проблема заключается в том, что ListT не является экземпляр MFunctor. Я думаю, что это автор бойкотирует ListT, потому что doesn't follow the monad transformer laws, хотя, потому что это легко написать экземпляр типа проверки

instance MFunctor ListT where hoist nat (ListT mas) = ListT (nat mas) 

Во всяком случае, как правило, посмотри на Control.Monad.Morph для работы с естественными преобразованиями на (части) трансформаторные стеки. Я бы сказал, что это соответствует определению подъема функции «достаточно» в стек.

+1

Да, я бойкотировал ListT. Используйте 'pipe' для правильного ListT. Кроме того, вы можете использовать 'fmapLT' из пакета ошибок для изменения значения слева. –

+0

Я искал 'fmapLT' ...! Но я мог бы поклясться, что это было в «любом», и не сделал этого. –

+0

Отлично; Спасибо вам обоим. Это то, о чем мне было интересно. :) – tvynr

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