再次感谢您的帮助!
我广泛使用了 E. Kmett 的镜头库,为了避免 X/Y 问题,我将解释一些上下文。
我正在开发一个可扩展的文本编辑器,并希望为扩展编写器提供 monad DSL,Alteration
是一个 monad 转换器堆栈,在Store
类型上具有 StateT,它基本上存储整个文本编辑器。Store
里面是一个Editor
,有Buffer
。用户可以指定一个Alteration
来对整个商店进行操作,但为了简化事情,我还提供了一个仅在单个缓冲区上运行的BufAction
。
我计划通过使用一个名为bufDo
的帮助程序来实现这一点,该助手在每个Buffer
上运行一个BufAction
,以及一个在"集中"Buffer
上运行BufAction
的focusDo
。以下是一些上下文:
data Store = Store
{ _event :: [Event]
, _editor :: E.Editor
, _extState :: Map TypeRep Ext
} deriving (Show)
data Editor = Editor {
_buffers :: [Buffer]
, _focused :: Int
, _exiting :: Bool
} deriving Show
data Buffer = Buffer
{ _text :: T.Text
, _bufExts :: Map TypeRep Ext
, _attrs :: [IAttr]
}
newtype Alteration a = Alteration
{ runAlt :: StateT Store IO a
} deriving (Functor, Applicative, Monad, MonadState Store, MonadIO)
newtype BufAction a = BufAction
{ runBufAction::StateT Buffer IO a
} deriving (Functor, Applicative, Monad, MonadState Buffer, MonadIO)
这是我为bufDo
和focusDo
提出的实现:
bufDo :: ???
bufDo = zoom (buffers.traverse)
-- focusedBuf is a Lens' over the focused buffer (I just 'force' the traversal using ^?! in case you're wondering)
focusDo :: ???
focusDo = zoom focusedBuf
这在我的脑海中是有道理的,并且接近类型检查,但是当我尝试为它们添加类型时,我有点困惑,ghc 建议了一些事情,我最终得到了这个,这远非优雅:
bufDo :: (Applicative (Zoomed BufAction ()), Zoom BufAction Alteration Buffer Store) => BufAction () -> Alteration ()
focusDo :: (Functor (Zoomed BufAction ()), Zoom BufAction Alteration Buffer Store) => BufAction () -> Alteration ()
这让ghc对这些定义感到满意,但是当我尝试实际使用其中任何一个时,我会收到以下错误:
- No instance for (Functor (Zoomed BufAction ()))
arising from a use of ‘focusDo’
- No instance for (Applicative (Zoomed BufAction ()))
arising from a use of ‘bufDo’
环顾四周似乎我可能需要为 Zoom 指定一个实例,但我不确定如何做到这一点。
有人有想法吗?如果您能解释为什么我需要 Zoom 实例(如果是这种情况),我也很高兴。
干杯!
似乎有一个Zoomed
类型系列用于指定我们在缩放时将具有什么样的"效果"。在某些情况下,monad 变压器的Zoomed
类型实例似乎搭载在底层 monad 的Zoomed
上,例如
type Zoomed (ReaderT * e m) = Zoomed m
鉴于Alteration
和BufAction
只是状态转换器上的新类型,也许我们可以做同样的事情:
{-# language TypeFamilies #-}
{-# language UndecidableInstances #-}
{-# language MultiParamTypeClasses #-}
type instance Zoomed BufAction = Zoomed (StateT Buffer IO)
然后我们必须提供Zoom
实例。Zoom
是一个多参数类型类,四个参数似乎是原始 monad、缩小 monad、原始状态、缩小状态、缩小状态:
instance Zoom BufAction Alteration Buffer Store where
zoom f (BufAction a) = Alteration (zoom f a)
我们只需解开BufAction
,使用底层 monad 缩放,然后包装为Alteration
.
此基本测试类型检查:
foo :: Alteration ()
foo = zoom (editor.buffers.traversed) (return () :: BufAction ())
我相信您可以避免定义Zoom
实例并拥有一个特殊用途的zoomBufActionToAlteration
函数
zoomBufActionToAlteration :: LensLike' (Zoomed (StateT Buffer IO) a) Store Buffer
-> BufAction a
-> Alteration a
zoomBufActionToAlteration f (BufAction a) = Alteration (zoom f a)
但是,如果您有许多不同的可缩放内容,那么记住每个缩放功能的名称可能是一件苦差事。这就是类型类可以提供帮助的地方。
作为答案@danidiaz的补充。
基本上,您可以通过以下方式避免Zoom
实例:
bufDo :: BufAction () -> Alteration ()
bufDo = Alteration . zoom (editor . buffers . traverse) . runBufAction