缩放monad堆栈维护上下文

时间:2017-01-12 22:27:01

标签: haskell lens lenses

我每周镜片问题的时间;

我有一个monad堆栈:

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

我在Editor类型上使用了makeClassy来生成我的编辑镜头所依赖的HasEditor类型类。

Editor有很多Buffer个;我为一个特定缓冲区(BufAction)上的动作定义了另一个monad堆栈类型;唯一的区别是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内,我无法再使用任何在editorHasEditor上运行的镜头。

理想情况下,所有Action都在BufAction内运行而不会提升,而BufAction无法在Action内运行。在这种情况下,BufAction将需要完整的ActionState,但也需要对特定缓冲区的引用才能运行;而Action只需要ActionState;所以BufAction是一个限制性较强的Monad,Action应该嵌入其中。

所以我想要某种类型的这种类型:

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对此嗤之以鼻;它无法处理新类型中的存在和约束;

我将其切换为data类型;但后来我失去了GeneralizedNewtypeDeriving并需要实现所有这些派生 手动条款;我真的不愿意这样做。

我也尝试过使用类型别名;这意味着我不需要派生类型类,但因为我也嵌入了动作 在其他数据类型中,我遇到了错误;例如,因为我在这里使用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使用功能依赖...

真的坚持这个,我可以用一只手!

谢谢你看看!我非常感谢你的帮助!

1 个答案:

答案 0 :(得分:0)

看起来这就是你要对我做的事情。关于Action中可接受哪种状态的约束将在使用Action而不是action本身的定义上指定。然后,您就可以使用镜头包中的zoom功能,专注于Buffer中的不同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