再次感谢您的帮助!
我正在广泛使用E. Kmett的Lens库,为避免X / Y问题,我将解释一些上下文。
我正在开发一个可扩展的文本编辑器,并希望为扩展编写者提供monad DSL,Alteration
是monad转换器堆栈,其StateT在Store
类型上,基本上存储了整个文本编辑器。 Store
内部是具有Editor
的Buffer
。用户可以指定一个Alteration
来作用于整个商店,但是为了简化起见,我还提供了一个BufAction
,它仅在单个缓冲区上运行。
我打算通过使用一个名为bufDo
的帮助程序来实现此目的,该帮助程序在每个BufAction
上运行一个Buffer
,并在上运行一个focusDo
的BufAction
。这里是一些上下文:
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)
这是我为
Buffer
和bufDo
建议的实现: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)
但是,如果您有很多不同的可缩放对象,记住每个缩放函数的名称可能会很麻烦。这就是类型类可以提供帮助的地方。