Как вы связаны выше, есть MonadReader
экземпляр для Free
:
instance (Functor m, MonadReader e m) => MonadReader e (Free m) where
, что это говорит о том, что, учитывая, что m
является Functor
и что есть MonadReader e
экземпляр для m
, мы также можем использовать экземпляра MonadReader
внутри Free
. Но для этого требуется, что уже есть пример MonadReader
для m
, который в вашем случае является вашим DSL Functor. Обычно это не, что вы хотите, потому что это радикально ограничивает доступные варианты для вашего функтора DSL, учитывая, что уже недостаточно быть функтором, а также должно быть монада.
Я бы предложил вместо использования Free (ReaderT r DSL) a
вы могли бы просто сложить его наоборот, то есть ReaderT r (Free DSL) a
, который имеет то преимущество, что DSL
должен быть только функтором. Для того, чтобы сделать это более конкретным, и учитывая, что вы не указать, как ваш DSL выглядит, давайте использовать пример Teletype
DSL:
data TeletypeF a = GetChar (Char -> a) | PutChar Char a deriving Functor
type Teletype a = Free TeletypeF a
getChar :: Teletype Char
getChar = liftF (GetChar id)
putChar :: Char -> Teletype()
putChar c = liftF (PutChar c())
putStrLn :: String -> Teletype()
putStrLn str = traverse putChar str >> putChar '\n'
runTeletype :: Teletype a -> IO a
runTeletype = foldFree go
where go (GetChar k) = k <$> IO.getChar
go (PutChar c k) = IO.putChar c >> return k
putStrLn
программа получена из DSL примитивного PutChar
. Мы можем интерпретировать программы, используя монаду IO
. Теперь мы хотим использовать монадный трансформатор ReaderT
, чтобы иметь возможность отложить выбор разделителя конца строки в putStrLn
. Таким образом, мы поступим следующим образом:
type TeletypeReader a = ReaderT Char (Free TeletypeF) a
getChar' :: TeletypeReader Char
getChar' = lift getChar
putChar' :: Char -> TeletypeReader()
putChar' c = lift (putChar c)
putStrLn' :: String -> TeletypeReader()
putStrLn' str = do
traverse_ putChar' str
sep <- ask
putChar' sep
runTeletypeReader :: Char -> TeletypeReader a -> IO a
runTeletypeReader sep = runTeletype . flip runReaderT sep
И теперь мы можем сделать:
λ> runTeletypeReader '\n' (putStrLn' "Hello" >> putStrLn' "World")
Hello
World
λ> runTeletypeReader ':' (putStrLn' "Hello" >> putStrLn' "World")
Hello:World:
Вы должны опубликовать точную ошибку. Бьюсь об заклад, это на самом деле несколько более конкретный: я уверен, он говорит, что нет экземпляра 'MonadReader' для' MyDSL', * not * для 'Free MyDSL', как вы утверждаете в вопросе. Это должен быть довольно сильный намек на то, что нужно для этого. –
@ DanielWagner У меня возникли проблемы с воспроизведением точной ошибки по разным причинам, но специфика не имеет большого значения. Я просто ищу любой пример, который объединяет любую mtl-монаду с Free. –
Вам понадобится «MyDSL» для экземпляра «MonadReader» для соответствующего типа среды. 'Free' не * добавляет * эффект читателя; он просто использует базовый. Если вы не можете опубликовать свой точный код и сообщение об ошибке, трудно понять, как мы можем вам помочь. – dfeuer