Часть того, что вы ищете, - это поднятие гомоморфизма монад в преобразователь монад.
class MonadHoist t where
hoist :: (Monad m, Monad n) => (forall a. m a -> n a) -> t m a -> t n a
t :: Monad m => t Identity a -> t m a
t = hoist (return . runIdentity)
То есть, учитывая гомоморфизм монад f
от m
до n
, вы можете получить гомоморфизм монад от t m
до t n
, используя подъемник.
Гомоморфизм монад немного сильнее, чем приведенные выше типы, а именно он отвечает за сохранение законов монады.
f . return = return
f . fmap g = fmap g . f
f . join = join . f . fmap f
= join . fmap f . f -- by the second law
= (>>= f) . f -- >>= in terms of join
Обратите внимание на квантификатор, который я выбрал в типе hoist
, MonadHoist
оказывается, что эта гибкость нужна почти для всех случаев!(Reader
оказывается единственным случаем, когда этого не происходит. Попробуйте написать MaybeT без него.)
В общем случае монадные преобразователи могут создавать этот класс.Например:
instance MonadHoist (StateT s) where
hoist f (StateT m) = StateT (f . m)
instance MonadHoist (ReaderT e) where
hoist f (ReaderT m) = ReaderT (f . m)
instance MonadHoist MaybeT where
hoist f (MaybeT m) = MaybeT (f m)
В настоящее время мы не предоставляем его в пакете transformers
или mtl
, потому что для этого потребуется Rank2Type
, но это довольно просто реализовать.
Если на него будет достаточно спроса, я с радостью упакую его в пакет monad-extras
.
Теперь, я сказал часть, потому что пока он отвечает на вопрос, заданный типом в теме вашегопост, это не удовлетворяет потребности, отраженной в объеме текста, связанного с вашим вопросом!
Для этого вы, вероятно, захотите последовать совету Луки.=)