2017-01-12 4 views
1

Temps pour ma question sur l'objectif hebdomadaire;Zoom monad stack maintien du contexte

J'ai une pile de monade:

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 utilisé makeClassy du type Editor pour générer un HasEditor que mes lentilles classe de types éditeur dépendent. Un Editor a de nombreux Buffer s; J'ai défini un autre type de pile monad pour une action qui agit sur un tampon spécifique (un BufAction); la seule différence est que le StateT est sur une Buffer:

newtype BufAction a = BufAction 
    { runBufAct::StateT Buffer (ReaderT Hooks IO) a 
    } deriving (Functor, Applicative, Monad, MonadState Buffer, MonadReader Hooks, MonadIO) 

pour lancer un BufAction J'utilise zoom (editor.buffers.ix selected) pour agrandir l'StateT à un tampon spécifique; mais le problème est que maintenant à l'intérieur du BufAction je ne peux plus utiliser les lentilles qui fonctionnent sur editor ou nécessitent HasEditor.

Idéalement tous Action Courons dans un BufAction sans soulever, alors que BufAction s ne peuvent pas courir à l'intérieur d'un Action. Dans ce cas BufAction nécessiterait le ActionState complet, mais aussi une référence à un tampon spécifique afin de fonctionner; tandis que Action nécessite simplement le ActionState; donc BufAction est une Monade plus restrictive et Action s devrait être embarquable dedans.

donc à peu près je veux une sorte de type comme ceci:

newtype Action a = forall s. HasEditor s => Action 
    { runAct :: StateT s (ReaderT Hooks IO) a 
    } deriving (Functor, Applicative, Monad, MonadState s, MonadReader Hooks, MonadIO) 

Cependant GHC selfs à ce sujet; il ne peut pas gérer les existentiels et les contraintes dans un newtype; Je l'ai changé pour un type data; mais alors je perds GeneralizedNewtypeDeriving et ai besoin de mettre en application tous ceux qui dérivent des clauses manuellement; que je préférerais ne pas faire.

J'ai également essayé d'utiliser un alias de type; ce qui signifierait que je n'ai pas besoin de dériver des typeclasses, mais puisque j'intègre aussi des Actions dans d'autres types de données, je rencontre des erreurs; par exemple depuis que je l'utilise Action ici:

data ActionState = ActionState 
{ _ed :: Editor 
, _asyncs :: [Async (Action())] 
, _hooks :: Hooks 
, _nextHook :: Int 
} 

je rencontre:

• 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’ 

Prendre un tact différent; J'ai aussi essayé la mise en œuvre d'une instance MonadState souple:

instance (HasEditor s, HasBuffer s) => (MonadState s) BufAction where 

mais je reçois:

• 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’ 

Parce que MonadState utilise des dépendances fonctionnelles ...

vraiment coincé sur celui-ci et je pourrais utiliser un main!

Merci d'avoir regardé! J'apprécie vraiment l'aide!

+0

Votre question n'est pas claire. Vous dites que "tout' l'Action fonctionne dans un 'BufAction' sans lever, alors que' BufAction's ne peut pas fonctionner dans une 'Action'" - comment cela fonctionnerait-il, quand l'état de 'BufAction' est plus petit que 'Action'' s état? Cela devrait sûrement être l'inverse? –

+0

Désolé que ce ne soit pas clair; Je vais essayer d'expliquer un peu mieux. Correct, BufAction utilise un état plus petit que «Action», mais ce n'est pas un «état plus petit» mais un état avec «plus d'informations». Nous pouvons penser à l'état disponible pour 'BufAction' comme' (ActionState, BufId) '; La totalité de 'ActionState' est disponible (tout comme' Action'), mais nous avons aussi des informations qui nous permettent de cibler un tampon spécifique. Ainsi, les 'actions 'qui opèrent sur' ActionState' devraient toujours être disponibles dans 'BufAction'; mais 'BufAction's requièrent le contexte' BufId' et ne peuvent pas fonctionner dans une 'Action'. Un plus clair? –

+0

Cela signifie bien sûr que nous devrons changer l'état dans lequel BufAction agit en quelque chose qui contient réellement l'état le plus grand; mais c'est ce à quoi renvoient les contraintes 'HasEditor' et' HasBuffer'; 'Action' devrait être exécuté sur un état avec seulement' HasEditor'; alors que 'BufAction' devrait être exécuté sur l'état avec' HasEditor' AND 'HasBuffer', c'est donc plus restrictif. –

Répondre

0

Il semble que c'est ce que vous essayez de me faire. Les contraintes concernant le type d'état acceptable dans une action seront spécifiées sur les définitions à l'aide de l'action, et non de l'action elle-même. Ensuite, vous serez en mesure d'utiliser la fonction zoom de l'ensemble de l'objectif pour se concentrer sur différents Buffer dans votre Editor, par exemple.

{-# 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, intéressant; ça pourrait marcher! –