開発中のドメイン固有の言語用に2つのモナドを書きました。最初はLang
です。これは、行ごとに言語を解析するために必要なものがすべて含まれているはずです。私はリーダー、ライター、および状態にしたいことを知っていたので、私はRWS
モナド使用:2つのモナド・トランススタックを組み合わせた場合、Applicativeを派生させることができません。
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) a }
deriving
(Functor
, Applicative
, Monad
, MonadIO
)
の両方がように見える:ユーザーと対話するために
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を使用しています(GHCiで振る舞いながら遊んだことがありますが)Lang
をRepl
に埋め込むことができず、ユーザーからの行を解析できませんでした。主な質問は、どうすればいいのですか?
具体的には、私は本来Lang
方法が含まれるようにRepl
を書いた場合:それは主にtypechecks
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
(Functor
, Applicative
, Monad
, MonadIO
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
が、私は(Monad
とすべての残りのために必要な)Applicative
を導出することはできません。
私はモナドトランスの新人でREPLを設計しているので、GlambdaのRepl.hs
とMonad.hs
から貨物を研究しています。もともと、私はGADTを私の表現にも使用しようとしていたので、それを選んだのです。 mzero
でREPLを終了できるようにする(これは危険なのですか?)
MaybeT
を
newtype
+GeneralizedNewtypeDeriving
:それは私が採用したが変更に完全に開いていました夫婦不慣れな慣行を含み、はここで、これまでの私の作業コードです:
{- 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 )
そしてカップルは、それを拡張しようとします。 、
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
でLang
含む-- 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()
が示されていません:私はまた
ask
にlift
の様々な順列を試してみましたが、putStrLn
が呼び出されます。newtype Lang2 a = Lang2 { unLang2 :: ReaderT LangConfig (WriterT LangLog (State LangState)) a } deriving (Functor , Applicative )
同じETA-削減エラーを与える:最後に、必ずすることは、これは、私はそれなしで
Lang
を書いてみましたRWS固有の問題ではありません。要するに、私が知りたいことは、これら2つのモナドをどのように組み合わせるかということです。
lift
の明白な組み合わせがないか、または変圧器スタックを間違って配置しているか、深刻な問題が発生していますか?ここで私が見夫婦おそらく関連の質問です:
- Tidying up Monads - turning application of a monad transformer into newtype monad
- Generalized Newtype DerivingGeneralized Newtype Deriving
- Issue deriving MonadTrans for chained custom monad transformers
更新:モナド変圧器の私の手波状の理解がメインでした問題。そう
LangT
はほとんどそれを解決Repl
とIO
の間に挿入することができるRWST
代わりにRWS
を使用: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 )
唯一残っている問題は、私は
MonadIO
ioのRepl2
インスタンスを作成する方法を理解する必要があります。更新2:今すぐすべて良いです!
MonadTrans
をLangT
のために派生したインスタンスのリストに追加するだけでした。
' IO'はhttp://stackoverflow.com/questions/13056663/why-is-there-no-io-(あなたのモナド変圧器スタックの一番下にある必要がありますトランスフォーマー・イン・ハスケル)。 'newtype LangT m a = LangT(RWST .. .. .. m a)のようなもの。 newtypeのREPLのA = REPL(MaybeT(InputT(LangT IO))a)は 'あなたのために働くかもしれません。 – user2407038
あなたは正しいです、ありがとう!私はIO'が下になければならない '知っていたが、何らかの理由では、スタック全体が直線的であるように私には発生していませんでした。私は、あなたが別のタイプの「オフサイド」を置くことができると思った。質問が更新されます。 – Jeff
'LangT'は、' MonadIO m => MonadIO(MaybeT m) 'インスタンスがそれを必要とするため、' MonadIO m => MonadIO(LangT m) 'インスタンス(これはおそらく導出可能です)を必要とします。 – user2407038