2013-08-21 1 views
6

Не так много, чтобы развернуть вопрос. Но вот случай использования: допустим, у вас есть две монады трансформаторы, t и s, преобразуя над одной и той же монады m:Есть ли принципиальный способ создания двух монадных трансформаторов, если они имеют разный тип, но их основная монада одного типа?

master :: (MonadTrans t, Monad m) => t m a b 
slave :: (MonadTrans t, Monad m) => s m a b 

И я хочу, чтобы составить master и slave таким образом, что они могут взаимодействовать друг с другом, когда m примитивы поднимаются на t и s. Подпись может быть:

bound :: (MonadTrans t, MonadTrans s, Monad m, Monoid a) => t m a b -> s m a b -> (...) 
But what is the type of (...) ? 

Прецедент, в подслащенной нотации:

master :: Monoid a => a -> t m a b 
master a = do 
    a <- lift . send $ (a,False)  -- * here master is passing function param to slave 
    ...        -- * do some logic with a 
    b <- lift . send $ (mempty,True) -- * master terminates slave, and get back result 

slave :: Monoid a => (a -> b) -> s m a b 
slave g = do 
    (a,end) <- lift receive 
    case end of 
     True -> get >>= \b -> exit b 
     _ -> (modify (++[g a])) >> slave g 

Update: send и receive примитивы типа m.

Прошу прощения, если этот пример выглядит надуманным или слишком похож на сопрограммы, дух вопроса на самом деле не имеет к этому никакого отношения, поэтому, пожалуйста, игнорируйте все сходства. Но главное, что монады t и s не могли быть разумно составлены друг с другом раньше, но после того, как обе обернут некоторую основную монаду m, они теперь могут быть составлены и выполняться как одна функция. Что касается типа скомпонованной функции, я действительно не уверен, поэтому какое-то направление ценится. Теперь, если эта абстракция уже существует, и я просто не знаю об этом, тогда это было бы лучше.

+0

'' '' '' '' '' '' 'или они как-то специфичны - мы пытаемся создать такие' '' и 't'? И тот же вопрос относится к 'm' - как насчет его и его' send' и 'receive'? –

+0

Да 's' и' t' являются произвольными. 'm' - это конкретный тип, который мы пытаемся создать здесь. 'send' и' receive' являются просто примитивными функциями типа '(a, Bool) -> m a b' и' m a b' соответственно. Но они связаны с надуманным прецедентом, который я дал, подробности их реализации не важны. – chibro2

ответ

8

Да. Объединить hoist из mmorph пакета с lift сделать это:

bound 
    :: (MonadTrans t, MonadTrans s, MFunctor t, Monad m) 
    => t m() -> s m() -> t (s m)() 
bound master slave = do 
    hoist lift master 
    lift slave 

Чтобы понять, почему это работает, изучить тип hoist:

hoist :: (MFunctor t) => (forall x . m x -> n x) -> t m r -> t n r 

hoist позволяет изменять базовую монаду любой монады трансформатора, который реализует MFunctor (что больше всего).

Какой код для bound действительно имеет два монадных трансформатора, согласных на конечную целевую монаду, которая в этом случае равна t (s m). Заказ, в котором вы гнездились t и s, зависит от вас, поэтому я просто предположил, что вам нужно t снаружи.

Тогда это просто вопрос использования различных комбинаций hoist и lift, чтобы получить два подвычисления, чтобы согласовать окончательный стек монады. Первый работает следующим образом:

master :: t m r 
hoist lift master :: t (s m) r 

второй один работает следующим образом:

slave :: s m r 
lift slave :: t (s m) r 

Теперь они оба согласны с тем, чтобы мы могли последовательно в пределах того же do блока, и он будет «просто работать».

Чтобы узнать больше о том, как hoist работает, я рекомендую вам проверить the documentation для mmorph пакета, который имеет a nice tutorial в нижней части.