缩放 monad 堆栈维护上下文

Zoom monad stack maintaining context

我的每周镜头问题时间到了;

我有一个 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;我已经为作用于特定缓冲区的操作定义了另一个 monad 堆栈类型 (a BufAction);唯一的区别是 StateT 在 Buffer:

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

到 运行 a BufAction 我使用 zoom (editor.buffers.ix selected) 将 StateT 缩放到特定缓冲区;但问题是,现在在 BufAction 内,我不能再使用任何超过 editor 或需要 HasEditor.

的镜头

理想情况下,BufAction 内的所有 Actions 运行 都没有提升,而 BufActions 不能 运行 在 Action 内。在这种情况下 BufAction 将需要完整的 ActionState,而且还需要对特定缓冲区的引用才能 运行;而 Action 只需要 ActionState;所以 BufAction 是一个更严格的 Monad 并且 Actions 应该可以嵌入其中。

大概我想要这样的类型:

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 并且需要实现所有这些派生 手动条款;我真的不想这样做。

我也试过使用类型别名;这意味着我不需要派生类型类,但因为我还嵌入了 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 使用函数依赖...

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

感谢观看!非常感谢您的帮助!

看来这就是你想对我做的。关于 Action 中可接受的状态类型的约束将在使用 Action 的定义中指定,而不是 Action 本身。然后,您将能够使用镜头包中的 zoom 功能来聚焦 Editor 中的不同 Buffer,例如。

{-# 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