Объединение StateT и государственных монадов

Допустим, у меня есть функция

f :: State [Int] Int

и функция:

g :: StateT [Int] IO Int

Я хочу использовать f в g и передать состояние между ними. Есть ли функция библиотеки для
StateT (return . runState f)? Или, вообще говоря, с учетом монадного трансформатора с соответствующей монадой, есть ли для него библиотечная функция?

Ответы

Ответ 1

В еще более общем виде то, что вы пытаетесь сделать, это применить преобразование к внутреннему слою стека трансформатора. Для двух произвольных монад подпись типа может выглядеть примерно так:

fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 a) -> t m1 a -> t m2 a

В основном более высокий уровень fmap. На самом деле, вероятно, было бы еще более целесообразно объединить его с картой над конечным параметром:

fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 b) -> t m1 a -> t m2 b

Ясно, что это не будет возможным во всех случаях, хотя, когда "исходная" монада Identity, это, вероятно, будет проще, но я могу представить, как определить класс другого типа для мест, в которых он работает. Я не думаю, что в типичных монадных трансформаторных библиотеках есть что-то подобное; однако некоторые просмотры взлома показывают что-то очень похожее в пакете Monatron:

class MonadT t => FMonadT t where
    tmap' :: FunctorD m -> FunctorD n -> (a -> b) 
             -> (forall x. m x -> n x) -> t m a -> t n b

tmap :: (FMonadT t, Functor m, Functor n) => (forall b. m b -> n b) 
        -> t m a -> t n a
tmap = tmap' functor functor id

В сигнатуре для tmap' типы FunctorD являются в основном ad-hoc реализациями fmap вместо непосредственного использования экземпляров Functor.

Кроме того, для двух конструкторов F-типа типа Functor функция с типом типа (forall a. F a -> G a) описывает естественное преобразование из F для G. Возможно, существует еще одна реализация карты трансформатора, которую вы хотите где-то в пакете category-extras, но я не уверен, что теоретическая версия трансформатора монады будет так, что я не знаю, что это может быть называется.

Так как tmap требует только экземпляр Functor (который должен иметь любой Monad) и естественное преобразование, а любое Monad имеет естественное преобразование из монады Identity, предоставляемой return, функция, которую вы хотите, может быть записана в общем случае для любого экземпляра FMonadT как tmap (return . runIdentity) - если предположить, что "базовая" монада определяется как синоним трансформатора, примененного к Identity, во всяком случае, как правило, имеет место с трансформаторных библиотек.

Возвращаясь к вашему конкретному примеру, обратите внимание, что у Monatron действительно есть экземпляр FMonadT для StateT.

Ответ 2

Такая функция не определена для всех монадных трансформаторов. Монада Cont r, например, не может быть поднята на ContT r IO, потому что это потребует превращения продолжения в монаду IO (a -> IO r) в чистое продолжение (a -> r).

Ответ 3

То, о чем вы просите, - это отображение (известное как морфизм монады) от монады StateT m до StateT n. Я буду использовать библиотеку mmorph, которая предоставляет очень хороший набор инструментов для работы с монадическими морфизмами.

Чтобы выполнить преобразование State -> StateT m, которое вы ищете, мы начнем с определения морфизма для обобщения монады Identity, встроенной в State,

generalize :: Monad m => Identity a -> m a
generalize = return . runIdentity

Затем мы захотим снять этот морфизм, чтобы воздействовать на внутреннюю монаду вашего StateT. То есть, мы хотим, чтобы функция, которая давала отображение от одной монады к другой (например, наш generalize -морфизм), даст нам функцию, действующую на базовую монаду монадного трансформатора, например. t Identity a -> t m a. Вы найдете, что это похоже на функцию hoist класса mmorph MFunctor,

hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b

Объединяя куски,

myAction :: State s Int
myAction = return 2

myAction' :: Monad m => StateT s m Int
myAction' = hoist generalize myAction