Ich habe zwei Monaden für eine domänenspezifische Sprache geschrieben, die ich entwickle. Die erste ist Lang
, die alles enthalten soll, was benötigt wird, um die Sprache Zeile für Zeile analysieren zu können. Ich wusste, ich möchte Leser, Schreiber und Staat, so habe ich die RWS
Monade:Applicative kann nicht abgeleitet werden, wenn zwei Monad-Transformer-Stacks kombiniert werden
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
)
Die zweite ist Repl
, die Haskeline mit einem Benutzer interagieren verwendet:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) a }
deriving
(Functor
, Applicative
, Monad
, MonadIO
)
Beide scheinen zu arbeiten einzeln (sie kompilieren und ich habe mit ihrem Verhalten in GHCi herumgespielt), aber ich bin nicht in der Lage gewesen, Lang
in Repl
einzubetten, um Linien vom Benutzer zu analysieren. Die Hauptfrage ist, wie kann ich das tun?
Genauer gesagt, wenn ich Repl
schreiben Lang
den Weg schließen ich ursprünglich gedacht:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
(Functor
, Applicative
, Monad
, MonadIO
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
Es meist typechecks, aber ich kann Applicative
(für Monad
und die ganzen Rest erforderlich) nicht ableiten.
Da ich Monod Transformatoren neu bin und REPLs entwerfe, habe ich von Glambda 's Repl.hs
und Monad.hs
studiert/Fracht-Kult. Ich habe es ursprünglich ausgewählt, weil ich versuchen werde, GADTs auch für meine Ausdrücke zu verwenden. Es enthält ein paar ungewohnte Praktiken, die ich angenommen habe, aber sind völlig offen für Veränderung:
newtype
+GeneralizedNewtypeDeriving
(das ist gefährlich?)MaybeT
zu erlauben, die REPL Aufhören mitmzero
Hier ist mein Arbeits Code so weit:
{- 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
)
Und ein paar versucht, sie zu verlängern. Erstens, einschließlich Lang
in Repl
wie oben erwähnt:
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’
Als nächstes versuchen, nur sie beide auf einmal verwenden:
-- 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()
Nicht gezeigt: Ich habe auch versucht, verschiedene Permutationen von lift
auf dem ask
und putStrLn
Anrufe. Schließlich sicher sein dies kein RWS-spezifisches Problem ist habe ich versuchte Lang
ohne es zu schreiben:
newtype Lang2 a = Lang2
{ unLang2 :: ReaderT LangConfig (WriterT LangLog (State LangState)) a
}
deriving
(Functor
, Applicative
)
, dass die gleichen eta-reduzieren Fehler gibt.
Also zur Zusammenfassung, die Hauptsache, die ich wissen möchte, ist, wie kombiniere ich diese zwei Monaden? Fehle ich eine offensichtliche Kombination von lift
s oder die Anordnung der Transformatorstapel falsch oder in ein tieferes Problem läuft?
Hier sind ein paar möglicherweise im Zusammenhang mit Fragen, die ich betrachten:
- 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
Update: meine Hand wellig Verständnis von Monade Transformatoren war die Haupt Problem. Mit RWST
statt RWS
so LangT
zwischen Repl
und IO
meist löst es eingefügt:
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
)
Die einzige noch offene Frage ist, ich brauche, um herauszufinden, wie man Repl2
eine Instanz io MonadIO
.
Update 2: Alles gut jetzt! Nur benötigt, um MonadTrans
zu der Liste der für LangT
abgeleiteten Instanzen hinzuzufügen.
'IO' muss sich am unteren Ende des Monad-Transformators befinden, da dort kein' IOT'-Monodentrafo vorhanden ist (http://Stackoverflow.com/questions/13056663/why-is-there-no-io-) Transformator-in-Haskell). Etwas wie 'newtype LangT m a = LangT (RWST .. .. .. m a); newtype Repl a = Repl (MaybeT (InputT (LangT IO)) a) 'könnte für Sie arbeiten. – user2407038
Sie haben Recht, danke! Ich wusste, dass "IO" auf der Unterseite sein muss, aber aus irgendeinem Grund war mir nicht eingefallen, dass der ganze Stapel linear ist. Ich dachte, du könntest einen anderen Typ "auf die Seite legen". Wird die Frage aktualisieren. – Jeff
'LangT' benötigt eine' MonadIO m => MonadIO (LangT m) '-Instanz (die wahrscheinlich abgeleitet werden kann), weil die 'MonadIO m => MonadIO (MaybeT m) -Instanz dies erfordert. – user2407038