Как сделать рекурсивный бесплатный интерпретатор монад? - PullRequest
3 голосов
/ 18 июня 2020

В Free интерпретаторе монад , использующем iterT, я хотел бы иметь внутреннее состояние, но я не уверен, как это сделать, поскольку функция iterT обеспечивает продолжение f предварительно загружен рекурсивным вызовом, насколько я понимаю. Я полагаю, что оболочка StateT - возможное решение (?), Но было бы неплохо избежать, если это возможно. Спасибо.

Edit: Чтобы уточнить, внутреннее состояние - это параметр mval, переданный в inner fun c. Я хочу выделить ресурс и продолжить интерпретатор с ресурсом.

import Control.Monad.Trans.Free.Church
import Control.Monad.Free.TH

type SomeFree m = FT SomeFreeF m
data SomeFreeF next = SomeAct (() -> next)
deriving instance (Functor SomeFreeF)
makeFree ''SomeFreeF

runSomeFree :: SomeFree IO () -> IO ()
runSomeFree = inner Nothing
  where
  inner mval =
    iterT \case
      SomeAct f -> do
        case mval of
          Nothing -> do
            a <- init
            inner (Just a) (FT someAct f ??)
                       -- How to continue the inner loop with    
                       -- the new state and the continuation `f`?
          Just a -> do
            f a

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Как я отмечал в комментариях, сначала blu sh это похоже на работу для iterTM, которая похожа на iterT, за исключением того, что она выполняется в преобразователе монад по вашему выбору.

iterTM :: (Functor f, Monad m, MonadTrans t, Monad (t m)) => (f (t m a) -> t m a) -> FreeT f m a -> t m a
iterTM f (FreeT m) = do  -- running in the output monad `t`
    val <- lift m
    case fmap (iterTM f) val of  -- fold the children first
        Pure x -> return x
        Free y -> f y

Вы можете выбрать выходную монаду t, но m и a определяются структурой данных FreeT, которую вы сворачиваете. Для каждого слоя FreeT, начиная с самого низа, iterTM передает f, полный результатов сворачивания дочерних элементов слоя в ваш обратный вызов. Вы должны решить, как обрабатывать эти результаты monadi c (выбирать между ними, упорядочивать их и т. мне больше нравится ReaderT. (Вы не возвращаете измененное состояние из каждой итерации - просто передаете измененный параметр вниз.)

runSomeFree :: Monad m => SomeFree m a -> ReaderT (Maybe ()) m a
runSomeFree = iterTM go
    where
        go (SomeAct f) = ask >>= \case
            Just () -> f ()
            Nothing -> local (const $ Just ()) (f ())
0 голосов
/ 19 июня 2020

Я принимаю ответ Бенджамина как правильный, поскольку это, пожалуй, самый простой / лучший ответ на вопрос, но в итоге я обнаружил, что монада FT не облегчила мой вариант (-ы) использования. Однако operational и monad-skeleton действительно позволяют этот вариант использования довольно легко , поскольку они не направляют интерпретатор через функцию привязки. С последним также весело и весело работать.

...