Как использовать состояние с трубами?

У меня есть функция типа Map Int String -> Proxy () a () Void IO b. Прямо сейчас он awaits делает все, что угодно, с полученным значением, а затем повторно вызывает себя. Я хотел бы изменить его так, чтобы он использовал State (Map Int String) вместо того, чтобы передавать это в качестве аргумента, поэтому я могу просто использовать forever и не нужно, чтобы каждая ветвь помнила о рекурсии. Я понимаю, что мне нужно использовать StateT для объединения State с другой монадой, но я не понимаю, где в этой сигнатуре типа принадлежит StateT и нужны ли мне lift функции, такие как get. Какой правильный тип для функции, которая одновременно является State (Map Int String) и Proxy () a () Void IO b?


person user3261399    schedule 13.02.2014    source источник
comment
Надеюсь это поможет! stackoverflow.com/ questions / 18471706 /   -  person user3303858    schedule 13.02.2014


Ответы (1)


Примечание: Proxy () a () Void = Consumer a, поэтому в этом ответе я буду называть его Consumer.

Самый простой способ - поместить слой преобразователя монад StateT за пределы слоя Consumer и сразу же запустить его. Вот пример:

import Control.Monad (forever)
import Control.Monad.Trans.State.Strict
import Pipes

example :: (Show a) => Consumer a IO r
example = flip evalStateT 0 $ forever $ do
    -- Inside here we are using `StateT Int (Consumer a IO) r`
    a <- lift await
    n <- get
    lift $ lift $ putStrLn $ "Received value #" ++ show n ++ ": " ++ show a
    put (n + 1)

... и вот как он ведет себя в действии:

>>> runEffect $ each ["Test", "ABC"] >-> example
Received value #0: "Test"
Received value #1: "ABC"
person Gabriel Gonzalez    schedule 13.02.2014