Как называется эта функция стека монад? - PullRequest
7 голосов
/ 11 мая 2011

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

mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a

Для простоты я не хочу передаватьКонтекст ввода-вывода в основной набор функций, и я хотел бы избежать оборачивания их в тип стека монады.Но чтобы вызвать их из функции верхнего уровня, мне нужно что-то похожее на лифт, но я не пытаюсь поднять значение из внутренней монады.Скорее я хочу преобразовать состояние в монаде StateT в нечто эквивалентное в монаде State.Чтобы сделать это, у меня есть следующее:

wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
              let (r,s2) = runState f s 
              put s2
              return r

Затем привыкаем чередовать такие вещи, как:

toplevel = do liftIO $ Some IO functions
              wrapST $ Some state mutations
              liftIO $ More IO functions
              ....

Это кажется довольно очевидным блоком кода, поэтому ямне интересно, есть ли у этой функции стандартное имя, и она уже реализована где-то в стандартных библиотеках?Я постарался сохранить описание простым, но, очевидно, это распространяется на извлечение одного преобразователя из стека, преобразование обернутого значения в двоюродного брата типа преобразователя, пропуск монад ниже в стеке, а затем отправку результатов обратно вконец.

Ответы [ 2 ]

9 голосов
/ 11 мая 2011

Это может быть хорошей идеей для рефакторинга вашего кода, чтобы использовать тип StateT SomeType m a вместо State SomeType a, потому что первый совместим с произвольным стеком монад.Если вы измените его следующим образом, вам больше не нужна функция wrapST, поскольку вы можете напрямую вызывать функции с состоянием.

Хорошо.Предположим, у вас есть функция subOne :: Monad m => State Int Int:

subOne = do a <- get
            put $ a - 1
            return a

Теперь измените типы всех функций, подобных этой, с State SomeType a на StateT SomeType m a, оставив m как есть.Таким образом, ваши функции могут работать с любым монадическим стеком.Для тех функций, которые требуют ввода / вывода, вы можете указать, что монада внизу должна быть IO:

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

Теперь должна быть возможность использовать обе функции вместе:

-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
            put $ a - 1

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

toZero :: StateT Int IO ()
toZero = do subOne     -- A really pure function
            printState -- function may perform IO
            a <- get
            when (a > 0) toZero

PS: я использую GHC 7, некоторые библиотеки изменились на полпути, поэтому на GHC 6 это может немного отличаться.

3 голосов
/ 28 июля 2015

Более прямой ответ на ваш вопрос: функция hoist делает именно то, что вы описываете, несколько более общим способом.Пример использования:

import Control.Monad.State
import Data.Functor.Identity
import Control.Monad.Morph

foo :: State Int Integer
foo = put 1 >> return 1

bar :: StateT Int IO Integer
bar = hoist (return . runIdentity) foo

hoist является частью класса MFunctor, который определяется следующим образом:

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

Существуют случаи для большинства монадных трансформаторов, но не1010 *.

...