我已经为我正在开发的特定领域的语言编写了两个 monad。第一个是Lang
,它应该包括逐行解析语言所需的所有内容。我知道我需要 reader、writer 和 state,所以我使用了RWS
monad:
type LangLog = [String]
type LangState = [(String, String)]
type LangConfig = [(String, String)]
newtype Lang a = Lang { unLang :: RWS LangConfig LangLog LangState a }
deriving
( Functor
, Applicative
, Monad
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
第二个是Repl
,它使用Haskeline与用户交互:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) a }
deriving
( Functor
, Applicative
, Monad
, MonadIO
)
两者似乎都可以单独工作(它们编译并且我已经在 GHCi 中尝试过它们的行为),但是我无法嵌入Lang
到Repl
解析用户的行中。主要问题是,我该怎么做?
更具体地说,如果我写Repl
的内容包括Lang
我最初打算的方式:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
( Functor
, Applicative
, Monad
, MonadIO
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
它主要是类型检查,但我无法派生Applicative
(Monad
其他所有内容都需要)。
由于我是 monad 转换器和设计 REPL 的新手,我一直在Glambda和. 我最初选择它是因为我也会尝试使用 GADT 来表达我的表达。它包括一些我已经采用但完全愿意改变的不熟悉的做法:Repl.hs
Monad.hs
newtype
+GeneralizedNewtypeDeriving
(这很危险吗?)MaybeT
允许退出 REPLmzero
到目前为止,这是我的工作代码:
{- LANGUAGE GeneralizedNewtypeDeriving #-}
module Main where
import Control.Monad.RWS.Lazy
import Control.Monad.Trans.Maybe
import System.Console.Haskeline
-- Lang monad for parsing language line by line
type LangLog = [String]
type LangState = [(String, String)]
type LangConfig = [(String, String)]
newtype Lang a = Lang { unLang :: RWS LangConfig LangLog LangState a }
deriving
( Functor
, Applicative
, Monad
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
-- Repl monad for responding to user input
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
( Functor
, Applicative
, Monad
, MonadIO
)
还有一些人试图扩展它。首先,包括Lang
在Repl
上面提到的:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
( Functor
, Applicative
)
-- Can't make a derived instance of ‘Functor Repl’
-- (even with cunning newtype deriving):
-- You need DeriveFunctor to derive an instance for this class
-- In the newtype declaration for ‘Repl’
--
-- After :set -XDeriveFunctor, it still complains:
--
-- Can't make a derived instance of ‘Applicative Repl’
-- (even with cunning newtype deriving):
-- cannot eta-reduce the representation type enough
-- In the newtype declaration for ‘Repl’
接下来,尝试同时使用它们:
-- Repl around Lang:
-- can't access Lang operations (get, put, ask, tell)
type ReplLang a = Repl (Lang a)
test1 :: ReplLang ()
test1 = do
liftIO $ putStrLn "can do liftIO here"
-- but not ask
return $ return ()
-- Lang around Repl:
-- can't access Repl operations (liftIO, getInputLine)
type LangRepl a = Lang (Repl a)
test2 :: LangRepl ()
test2 = do
_ <- ask -- can do ask
-- but not liftIO
return $ return ()
lift
未显示:我还尝试了on the ask
andputStrLn
调用的各种排列。最后,为了确保这不是 RWS 特有的问题,我尝试在Lang
没有它的情况下编写:
newtype Lang2 a = Lang2
{ unLang2 :: ReaderT LangConfig (WriterT LangLog (State LangState)) a
}
deriving
( Functor
, Applicative
)
这给出了相同的 eta-reduce 错误。
所以回顾一下,我想知道的主要事情是我如何结合这两个单子?我是否遗漏了一个明显的lift
s 组合,或者错误地安排了变压器堆栈,或者遇到了一些更深层次的问题?
以下是我查看的几个可能相关的问题:
更新:我对 monad 转换器的粗浅理解是主要问题。可以在两者之间插入usingRWST
而不是RWS
so并主要解决它:LangT
Repl
IO
newtype LangT m a = LangT { unLangT :: RWST LangConfig LangLog LangState m a }
deriving
( Functor
, Applicative
, Monad
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
type Lang2 a = LangT Identity a
newtype Repl2 a = Repl2 { unRepl2 :: MaybeT (LangT (InputT IO)) a }
deriving
( Functor
, Applicative
, Monad
-- , MonadIO -- ghc: No instance for (MonadIO (LangT (InputT IO)))
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
剩下的唯一问题是我需要弄清楚如何制作Repl2
实例 io MonadIO
。
更新 2:现在一切都好!只需要添加MonadTrans
到为LangT
.