Ho scritto due monadi per un linguaggio specifico del dominio che sto sviluppando. Il primo è Lang
, che dovrebbe includere tutto il necessario per analizzare la lingua riga per riga. Sapevo che vorrei lettore, scrittore, e lo stato, quindi ho usato il RWS
monade:Impossibile derivare Applicativo quando si combinano due pile di trasformatori 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
)
Il secondo è Repl
, che utilizza Haskeline di interagire con un utente:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) a }
deriving
(Functor
, Applicative
, Monad
, MonadIO
)
Entrambi sembrano lavoro individualmente (si compilano e ho giocato con il loro comportamento in GHCi), ma non sono riuscito a incorporare Lang
in Repl
per analizzare le righe dall'utente. La domanda principale è, come posso farlo?
Più in particolare, se scrivo Repl
includere Lang
il modo in cui originariamente destinati:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
(Functor
, Applicative
, Monad
, MonadIO
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
per lo più typechecks, ma non riesco a ricavare Applicative
(necessario per Monad
e tutto il resto).
Da quando sono nuovo ai trasformatori monad e alla progettazione di REPL, ho studiato/cargo-culting da Glambda's Repl.hs
e Monad.hs
. L'ho originariamente scelto perché proverò ad usare GADT anche per le mie espressioni. Esso comprende un paio di pratiche non familiari, che ho adottato, ma sono totalmente aperti a cambiare:
newtype
+GeneralizedNewtypeDeriving
(è questo pericoloso?)MaybeT
per consentire l'uscita dal REPL conmzero
Ecco il mio codice di lavoro finora:
{- 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
)
E un paio tentativi di estenderla. In primo luogo, compresi Lang
in Repl
come detto sopra:
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’
Avanti, cercando di usare appena entrambi contemporaneamente:
-- 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()
Non illustrato: Ho anche provato varie permutazioni di lift
sulla ask
e Chiamate putStrLn
. Infine, per essere sicuri che questo non è un problema specifico-RWS Ho provato a scrivere Lang
senza di essa:
newtype Lang2 a = Lang2
{ unLang2 :: ReaderT LangConfig (WriterT LangLog (State LangState)) a
}
deriving
(Functor
, Applicative
)
Questo dà lo stesso errore eta-ridurre.
Quindi per ricapitolare, la cosa principale che voglio sapere è come combinare queste due monadi? Mi manca una combinazione ovvia di lift
s, o che organizzo la pila del trasformatore sbagliata o che si sta verificando un problema più profondo?
Qui ci sono un paio di domande possibilmente correlate ho guardato:
- 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
Aggiornamento: la mia comprensione a mano ondulato di trasformatori monade è stato il principale problema. Utilizzando RWST
anziché RWS
così LangT
è inseribile tra Repl
e IO
risolve soprattutto è:
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
)
L'unica questione è devo capire come fare Repl2
un'istanza io MonadIO
.
Aggiornamento 2: Tutto bene ora! Basta aggiungere MonadTrans
all'elenco delle istanze derivate per LangT
.
'IO' deve trovarsi nella parte inferiore della pila di trasformatori monad perché non c'è un trasformatore monade' IOT' (http://stackoverflow.com/questions/13056663/why-is-there-no-io- trasformatore-in-haskell). Qualcosa come 'newtype LangT m a = LangT (RWST .. ..m a); newtype Repl a = Repl (MaybeT (InputT (LangT IO)) a) 'potrebbe funzionare per voi. – user2407038
Hai ragione, grazie! Sapevo che 'IO' doveva essere in fondo, ma per qualche motivo non mi era venuto in mente che l'intero stack fosse lineare. Pensavo che potessi mettere un altro tipo di "off to side". Aggiornerà la domanda – Jeff
'LangT' ha bisogno di un'istanza' MonadIO m => MonadIO (LangT m) '(che può probabilmente essere derivata) perché l'istanza' MonadIO m => MonadIO (MaybeT m) 'lo richiede. – user2407038