2017-01-12 4 views
1

Время для моего вопроса об объективе в неделю;Содержит контекст сохранения штатов монады

У меня есть стек монады:

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] 
    } 

я использовал makeClassy на Editor типа для создания HasEditor класса типов, которые мой редактор линзы зависит от.

Editor имеет множество Buffer s; Я определил другой тип стека монады для действия, которое действует над определенным буфером (BufAction); единственное различие заключается в том, что StateT находится над Buffer:

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

запустить BufAction я использую zoom (editor.buffers.ix selected), чтобы увеличить StateT к определенному буфера; но проблема в том, что теперь внутри BufAction я больше не могу использовать какие-либо объективы, которые работают за editor, или требуют HasEditor.

В идеале все Action ы работать внутри BufAction без подъема, в то время как BufAction s не может работать внутри Action. В этом случае BufAction потребует полного ActionState, но также ссылки на конкретный буфер для запуска; тогда как Action просто требует ActionState; так что BufAction является более ограничительным Monad и Action s должен быть внедрен в него.

Так примерно я хочу какой-то тип, как это:

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

Однако GHC дросселей на этом; он не может обрабатывать экзистенции и ограничения в newtype;

Я переключил его на тип data; но затем я теряю GeneralizedNewtypeDeriving и должен реализовать все из тех, которые выводят статьи вручную; который я бы действительно не сделал.

Я также пробовал использовать псевдоним типа; что означало бы, что мне не нужно выводить классы, но поскольку я также вставляю Actions в другие типы данных, я сталкиваюсь с ошибками; например, так как я использую Action здесь:

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

Я бегу в:

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

Принимая другую тактичность; Я также попытался реализацией гибкого экземпляра MonadState:

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

но получить:

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

Поскольку MonadState использует функциональную зависимость ...

Действительно застрял на этом, и я мог бы использовать рука!

Спасибо, что посмотрели! Я очень ценю помощь!

+0

Ваш вопрос неясен. Вы говорите «все» действия '' BufAction' без подъема, в то время как 'BufAction' не может работать внутри 'Action' '- как это будет работать, когда состояние' BufAction' меньше, чем 'Action'' состояние? Неужели это должно быть наоборот? –

+0

Извините, что это неясно; Я попытаюсь объяснить немного лучше. Правильно, BufAction использует меньшее состояние, чем «Действие», но это не столько «меньшее состояние», сколько состояние с «большей информацией». Мы можем думать о состоянии, доступном для 'BufAction', как' (ActionState, BufId) '; по сути, доступно все «ActionState» (точно так же, как «Действие»), но у нас есть информация, которая позволяет нам также сфокусировать определенный буфер. Поэтому любые 'Action', которые работают над' ActionState', все равно должны быть доступны в 'BufAction'; но 'BufAction' требует контекста 'BufId' и не может работать в' Action'. Чем яснее? –

+0

Это означает, конечно, что нам нужно будет изменить состояние, которое BufAction действует на то, что на самом деле содержит большее состояние; но это то, к чему относятся ограничения HasEditor и 'HasBuffer'; «Действие» должно выполняться над состоянием только с «HasEditor»; в то время как 'BufAction' должен работать над состоянием с' HasEditor' AND 'HasBuffer', поэтому он более ограничительный. –

ответ

0

Похоже, это то, что вы пытаетесь сделать со мной. Ограничения относительно того, какое состояние приемлемо в Action, будут указаны в определениях с использованием Action, а не самого действия. Затем вы сможете использовать функцию zoom из комплекта объективов, чтобы сосредоточиться на разных Buffer s в вашем Editor, например.

{-# 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, интересно; это может сработать! –

Смежные вопросы