再次感谢您的帮助!

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

我正在开发一个可扩展的文本编辑器,并希望为扩展编写者提供monad DSL,Alteration是monad转换器堆栈,其StateT在Store类型上,基本上存储了整个文本编辑器。 Store内部是具有EditorBuffer。用户可以指定一个Alteration来作用于整个商店,但是为了简化起见,我还提供了一个BufAction,它仅在单个缓冲区上运行。

我打算通过使用一个名为bufDo的帮助程序来实现此目的,该帮助程序在每个BufAction上运行一个Buffer,并在上运行一个focusDoBufAction。这里是一些上下文:

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)


这是我为BufferbufDo建议的实现:

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)


但是,如果您有很多不同的可缩放对象,记住每个缩放函数的名称可能会很麻烦。这就是类型类可以提供帮助的地方。

10-06 15:44
查看更多