2017-01-12 4 views
1

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!

+0

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? –

+0

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? –

+0

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. –

Antwort

0

Es sieht so aus, als ob Sie das versuchen. Die Einschränkungen, welche Art von Status in einer Aktion akzeptabel ist, werden in Definitionen angegeben, die Aktion verwenden, nicht Aktion selbst. Dann können Sie die Funktion aus dem Objektivpaket verwenden, um beispielsweise auf andere Buffer s in Ihrer Editor zu fokussieren.

{-# Language TemplateHaskell #-} 
{-# Language GeneralizedNewtypeDeriving #-} 
{-# Language MultiParamTypeClasses #-} 
{-# Language FlexibleInstances #-} 
{-# Language TypeFamilies #-} 
{-# Language UndecidableInstances #-} -- for the Zoomed type instance 

module Demo where 

import Control.Monad.State 
import Control.Monad.Reader 
import Control.Lens 

data Hooks 

data SomeState = SomeState 
    { _thing1, _thing2 :: Int } 

makeLenses ''SomeState 

newtype Action s a = Action 
    { runAct :: StateT s (ReaderT Hooks IO) a 
    } deriving (Functor, Applicative, Monad, MonadState s) 

instance Zoom (Action a) (Action s) a s where 
    zoom l (Action m) = Action (zoom l m) 

type instance Zoomed (Action s) = Zoomed (StateT s (ReaderT Hooks IO)) 

example :: Action Int a -> Action SomeState a 
example = zoom thing1 
+0

Hrmm, interessant; das könnte funktionieren! –

Verwandte Themen