如何缩放单体变压器?



再次感谢您的帮助!

我广泛使用了 E. Kmett 的镜头库,为了避免 X/Y 问题,我将解释一些上下文。

我正在开发一个可扩展的文本编辑器,并希望为扩展编写器提供 monad DSL,Alteration是一个 monad 转换器堆栈,在Store类型上具有 StateT,它基本上存储整个文本编辑器。Store里面是一个Editor,有Buffer。用户可以指定一个Alteration来对整个商店进行操作,但为了简化事情,我还提供了一个仅在单个缓冲区上运行的BufAction

我计划通过使用一个名为bufDo的帮助程序来实现这一点,该助手在每个Buffer上运行一个BufAction,以及一个在"集中"Buffer上运行BufActionfocusDo。以下是一些上下文:

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)

这是我为bufDofocusDo提出的实现:

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

鉴于AlterationBufAction只是状态转换器上的新类型,也许我们可以做同样的事情:

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