如何缩放 monad 变压器?
How to zoom a monad transformer?
再次感谢您的帮助!
我正在广泛使用 E. Kmett 的 Lens 库,为避免 X/Y 问题,我将解释一些上下文。
我正在开发一个可扩展的文本编辑器,想为扩展作者提供一个 monad DSL,Alteration
是一个 monad 转换器堆栈,在 Store
类型上有一个 StateT,基本上存储整个文本编辑器。在 Store
内部是一个 Editor
,其中包含 Buffer
。用户可以指定一个 Alteration
来对整个存储进行操作,但为了简化操作,我还提供了一个 BufAction
,它只对一个缓冲区进行操作。
我计划通过使用一个名为 bufDo
的助手来实现这一点,它在每个 Buffer
上运行 BufAction
,而 focusDo
运行 [=20] =] 在 'focused' Buffer
上。这是一些背景信息:
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
类型族,用来指定我们缩放时会有什么样的"effect"。在某些情况下,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
是多参数类型类,四个参数好像是 original monad, zoomed out 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
再次感谢您的帮助!
我正在广泛使用 E. Kmett 的 Lens 库,为避免 X/Y 问题,我将解释一些上下文。
我正在开发一个可扩展的文本编辑器,想为扩展作者提供一个 monad DSL,Alteration
是一个 monad 转换器堆栈,在 Store
类型上有一个 StateT,基本上存储整个文本编辑器。在 Store
内部是一个 Editor
,其中包含 Buffer
。用户可以指定一个 Alteration
来对整个存储进行操作,但为了简化操作,我还提供了一个 BufAction
,它只对一个缓冲区进行操作。
我计划通过使用一个名为 bufDo
的助手来实现这一点,它在每个 Buffer
上运行 BufAction
,而 focusDo
运行 [=20] =] 在 'focused' Buffer
上。这是一些背景信息:
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
类型族,用来指定我们缩放时会有什么样的"effect"。在某些情况下,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
是多参数类型类,四个参数好像是 original monad, zoomed out 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