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