我如何利用MonadReader实例的免费



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

我注意到这里有一个MonadReader for Free实例:

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

如果我尝试在我的EDSL中编写的程序中调用ask,我得到类型错误"No such instance MonadReader Free MyDSL"

我最终想要使用其他monad与我的DSL,如MonadError和Logging monad,但Reader monad只是我尝试过的第一个。

正如您上面所链接的,Free有一个MonadReader实例:

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

这就是说,如果mFunctor,并且m有一个MonadReader e实例,我们也可以利用Free内部的MonadReader实例。但是这个要求已经有一个mMonadReader实例,在你的情况下是你的DSL函子。这通常不是你想要的,因为这极大地限制了DSL函子的可用选择,因为它不再是一个函子,而且必须是一个monad。

因此,我建议不要使用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:

相关内容

  • 没有找到相关文章

最新更新