如何利用Free的MonadReader实例?

时间:2015-09-16 23:21:43

标签: haskell free-monad reader-monad

我想在我使用Free monad创建的DSL中使用Reader。

我注意到这里有一个免费的MonadReader实例:

https://hackage.haskell.org/package/free-4.12.1/docs/src/Control-Monad-Free.html#line-264

如果我尝试在我的EDSL中编写的程序中调用ask,我会收到类型错误"没有这样的实例MonadReader Free MyDSL"

我最终会想要使用其他Monad和我的DSL,例如MonadError和Logging monad,但是阅读器monad只是我尝试过的第一个。

1 个答案:

答案 0 :(得分:3)

如上所述,MonadReader有一个Free个实例:

instance (Functor m, MonadReader e m) => MonadReader e (Free m) where

这说的是,mFunctor并且MonadReader em个实例,我们也可以使用{{1} MonadReader内的实例。但是要求已经存在Free MonadReader个实例,在您的情况下是您的DSL Functor。这通常是你想要的东西,因为这极大地限制了你的DSL仿函数的可用选择,因为它不再是一个仿函数而且必须一个单子。

因此,我建议您不要使用m,而是可以反过来将其分层,即Free (ReaderT r DSL) a,这样做的好处是ReaderT r (Free DSL) a只需要是一个仿函数。为了使这个更具体,并且考虑到你没有说明你的DSL是什么样的,让我们使用DSL DSL示例:

Teletype

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 是源自DSL原语putStrLn的程序。我们可以使用PutChar monad来解释程序。现在我们想要使用IO monad转换器来推迟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
相关问题