Объединение монад StateT и State

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

f :: State [Int] Int

и функция:

g :: StateT [Int] IO Int

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


person HaskellElephant    schedule 09.11.2010    source источник
comment
Я считаю, что редактирование TomMD неверно. Я считаю, что оригинал g :: StateT [Int] IO Int должен стоять.   -  person glguy    schedule 10.11.2010
comment
Остальные изменения понравились, скобки исправлены ...   -  person HaskellElephant    schedule 10.11.2010
comment
Этот вопрос кажется тем, что я ищу, но все ответы намного сложнее, чем stackoverflow.com/questions/17325485/, которое выполнило мою работу за меня.   -  person crockeea    schedule 29.09.2013


Ответы (3)


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

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 в основном представляют собой специальные реализации fmap вместо прямого использования экземпляров Functor.

Кроме того, для двух конструкторов типов F и G типа 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.

person C. A. McCann    schedule 09.11.2010
comment
Я не смотрел пакет Monatron. Мне придется присмотреться к этому, чтобы судить об этом. Мне нравится ваша идея определить класс типа, когда он действительно работает. Может ли кто-нибудь подтвердить или опровергнуть, что Monatron делает это? - person HaskellElephant; 10.11.2010

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

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

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

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

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
person bgamari    schedule 19.08.2013

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

person Heatsink    schedule 09.11.2010
comment
Не думал об этом. Как вы сказали, это возможно не для всех монадтрансформаторов. Значит, тогда потребуется особая связь между преобразователем и соответствующей монадой ... - person HaskellElephant; 10.11.2010
comment
Разве трансформация не пошла бы по другому пути? Поскольку r в типе продолжения обычно полиморфен, вы можете просто написать (ContT . runCont) :: Cont (m r) a -> ContT r m a. - person C. A. McCann; 10.11.2010
comment
@camccann Я собирался использовать обобщенный класс преобразований (Monad m = ›m a -› (TransformerOf m) m 'a) (немного злоупотребляя обозначениями). Если вы попытаетесь написать экземпляр для (Cont r a - ›ContT r m a), вы застрянете в точке, которую я описал. - person Heatsink; 10.11.2010