Объединение StateT и State монад - PullRequest
16 голосов
/ 10 ноября 2010

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

f :: State [Int] Int

и функция:

g :: StateT [Int] IO Int

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

Ответы [ 3 ]

5 голосов
/ 10 ноября 2010

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

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, вероятно, будет проще, но я могу себе представить определение другого класса типов для мест, где он работает.Я не думаю, что есть что-то подобное в типичных библиотеках монадных преобразователей;однако, некоторые просмотры на hackage обнаруживают нечто очень похожее в пакете 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 в основном являютсяhoc реализации fmap вместо непосредственного использования Functor.

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

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

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

4 голосов
/ 20 августа 2013

То, что вы просите, - это отображение (известное как морфизм монад) из монады 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
4 голосов
/ 10 ноября 2010

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

...