Существует ли принципиальный способ составления двух монадных преобразователей, если они разного типа, но лежащая в их основе монада того же типа?

Не так много я могу сделать, чтобы расширить вопрос. Но вот пример использования: допустим, у вас есть два монадных трансформатора, 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

Обновить: send а также receive примитивы типа m,

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

1 ответ

Решение

Да. скомбинировать 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 работает, я рекомендую вам проверить документацию для mmorph пакет, который имеет хороший учебник в нижней части.

Другие вопросы по тегам