Zeit für meine wöchentliche Objektivfrage;Zoom-Monad-Stapel, der den Kontext beibehält
Ich habe einen Monade Stapel:
newtype Action a = Action
{ runAct :: StateT ActionState (ReaderT Hooks IO) a
} deriving (Functor, Applicative, Monad, MonadState ActionState, MonadReader Hooks, MonadIO)
data ActionState = ActionState
{ _ed :: Editor
, _asyncs :: [AsyncAction]
}
I verwendet makeClassy
auf dem Editor
Typ eine HasEditor
typeclass, die Linsen mein Editor hängen von zu generieren.
Ein Editor
hat viele Buffer
s; Ich habe einen anderen Monadstack-Typ für eine Aktion definiert, die über einen bestimmten Puffer wirkt (a BufAction
); der einzige Unterschied besteht darin, dass die StateT über eine Buffer
ist:
newtype BufAction a = BufAction
{ runBufAct::StateT Buffer (ReaderT Hooks IO) a
} deriving (Functor, Applicative, Monad, MonadState Buffer, MonadReader Hooks, MonadIO)
ein BufAction
ich zoom (editor.buffers.ix selected)
die StateT zu einem bestimmten Puffer zu vergrößern verwenden zu laufen; aber das Problem ist, dass jetzt innerhalb der BufAction
kann ich keine Objektive mehr verwenden, die über editor
arbeiten oder erfordern HasEditor
.
idealerweise alle Action
s innerhalb eines BufAction
ohne Anheben laufen, während BufAction
s nicht innerhalb einer Action
laufen kann. In diesem Fall würde BufAction
die volle ActionState
erfordern, aber auch eine Referenz auf einen bestimmten Puffer, um zu laufen; während Action
benötigt nur die ActionState
; so BufAction
ist eine restriktivere Monade und Action
s sollte darin eingebettet werden.
So etwa möchte ich eine Art von Typ wie folgt aus:
newtype Action a = forall s. HasEditor s => Action
{ runAct :: StateT s (ReaderT Hooks IO) a
} deriving (Functor, Applicative, Monad, MonadState s, MonadReader Hooks, MonadIO)
jedoch GHC auf diese Drosseln; Es kann nicht mit Existenzen und Einschränkungen in einem neuen Typ umgehen;
Ich wechselte es zu einem data
Art; aber dann verliere ich GeneralizedNewtypeDeriving und muss alle diejenigen implementieren, die Klauseln manuell ableiten; was ich wirklich lieber nicht machen würde.
Ich habe auch versucht, einen Typalias zu verwenden; Das würde bedeuten, dass ich keine Typklassen ableiten muss, aber da ich auch die Aktionen in andere Datentypen einbetten kann, stosse ich auf Fehler; da ich zum Beispiel verwenden Action
hier:
data ActionState = ActionState
{ _ed :: Editor
, _asyncs :: [Async (Action())]
, _hooks :: Hooks
, _nextHook :: Int
}
Ich laufe in:
• Illegal polymorphic type: Action()
GHC doesn't yet support impredicative polymorphism
• In the definition of data constructor ‘ActionState’
In the data type declaration for ‘ActionState’
einen anderen Takt nehmen; Ich habe auch versucht, eine flexible MonadState Instanz Umsetzung:
instance (HasEditor s, HasBuffer s) => (MonadState s) BufAction where
aber erhalten:
• Illegal instance declaration for ‘MonadState s BufAction’
The coverage condition fails in class ‘MonadState’
for functional dependency: ‘m -> s’
Reason: lhs type ‘BufAction’ does not determine rhs type ‘s’
Un-determined variable: s
• In the instance declaration for ‘(MonadState s) BufAction’
Da MonadState funktionale Abhängigkeiten verwendet ...
wirklich auf diese stecken und ich konnte ein verwenden Hand!
Vielen Dank für einen Blick! Ich schätze die Hilfe sehr!
Ihre Frage ist unklar. Du sagst "all' Action's Arbeit in einem' BufAction' ohne anzuheben, während 'BufAction' nicht in einer' Action' laufen kann - wie würde das funktionieren, wenn 'BufAction's Zustand kleiner ist als' Action' ' s Zustand? Sicher sollte es umgekehrt sein? –
Entschuldigung, dass es unklar ist; Ich werde versuchen, es ein wenig besser zu erklären. Richtig, BufAction verwendet einen kleineren Zustand als "Aktion", aber es ist nicht so sehr ein "kleinerer Zustand" als ein Zustand mit "mehr Informationen". Wir können uns den Zustand vorstellen, der für BufAction als (ActionState, BufId) verfügbar ist. im Wesentlichen ist der gesamte "ActionState" verfügbar (genau wie "Action"), aber wir haben Informationen, die es uns ermöglichen, einen bestimmten Puffer zu fokussieren. Daher sollten alle 'Aktionen', die über 'ActionState' laufen, immer noch in 'BufAction' verfügbar sein; aber 'BufAction's erfordern den 'BufId'-Kontext und können nicht in einer' Aktion' laufen. Irgendein klarer? –
Dies bedeutet natürlich, dass wir den Zustand, in dem die BufAction wirkt, auf etwas ändern müssen, das tatsächlich den größeren Zustand enthält; aber darauf beziehen sich die "HasEditor" - und "HasBuffer" -Einschränkungen; "Aktion" sollte über einen Zustand mit nur "HasEditor" laufen; während 'BufAction' über den Zustand mit' HasEditor' UND 'HasBuffer' laufen sollte, ist es restriktiver. –