Haskell 如何缩放monad转换器?

Haskell 如何缩放monad转换器?,haskell,haskell-lens,state-monad,Haskell,Haskell Lens,State Monad,再次感谢你的帮助 我正在广泛使用E.Kmett的镜头库,为了避免X/Y问题,我将解释一些上下文 我正在开发一个可扩展文本编辑器,希望为扩展编写器提供一个monad DSL,转换是一个monad转换器堆栈,在存储类型上有一个StateT,它基本上存储整个文本编辑器。在存储中有一个编辑器,它具有缓冲区s。用户可以指定一个变更对整个商店进行操作,但为了简化操作,我还提供了一个变更,它只在一个缓冲区上操作 我计划通过使用名为bufDo的助手来实现这一点,该助手在每个Buffer上运行buparty,而f

再次感谢你的帮助

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

我正在开发一个可扩展文本编辑器,希望为扩展编写器提供一个monad DSL,
转换
是一个monad转换器堆栈,在
存储
类型上有一个StateT,它基本上存储整个文本编辑器。在
存储
中有一个
编辑器
,它具有
缓冲区
s。用户可以指定一个
变更
对整个商店进行操作,但为了简化操作,我还提供了一个
变更
,它只在一个缓冲区上操作

我计划通过使用名为
bufDo
的助手来实现这一点,该助手在每个
Buffer
上运行
buparty
,而
focusDo
在“聚焦的
Buffer
上运行
buparty
。以下是一些上下文:

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指定一个实例,但我不太确定如何做到这一点

有人有想法吗?如果你能解释为什么我需要一个缩放实例(如果是这样的话),我也会很高兴


干杯

似乎有一个
缩放的
类型族,用于指定缩放时的“效果”。例如,在某些情况下,monad transformer的
Zoomed
类型实例似乎与底层monad的
Zoomed
类型实例相匹配

type Zoomed (ReaderT * e m) = Zoomed m
鉴于
变更
变更
只是状态转换器上的新类型,或许我们也可以这样做:

{-# language TypeFamilies #-}
{-# language UndecidableInstances #-}
{-# language MultiParamTypeClasses #-}    

type instance Zoomed BufAction = Zoomed (StateT Buffer IO)
然后我们必须提供
Zoom
实例
Zoom
是一个多参数类型类,四个参数似乎是原始单子、缩小单子、原始状态、缩小状态:

我们只需展开
buaction
,缩放底层monad,并将其包装为
alternation

此基本测试类型检查:

foo :: Alteration ()
foo = zoom (editor.buffers.traversed) (return () :: BufAction ())

我相信您可以避免定义
Zoom
实例,并使用一个专用的
zoombulation
函数

zoomBufActionToAlteration :: LensLike' (Zoomed (StateT Buffer IO) a) Store Buffer 
                          -> BufAction a 
                          -> Alteration a
zoomBufActionToAlteration f (BufAction a) = Alteration (zoom f a)       

但是,如果你有很多不同的可缩放功能,记住每个缩放功能的名称可能是件麻烦事。这就是typeclass可以提供帮助的地方。

作为答案@danidiaz的补充


基本上,您可以通过以下方式避免
Zoom
实例:

bufDo :: BufAction () -> Alteration ()
bufDo = Alteration . zoom (editor . buffers . traverse) . runBufAction

这本质上是一个复制品。我看到了那个,但还是有点困惑,他们给出了一个解决方案,但没有解释清楚。我会试试这个!谢谢您能否更深入地解释一下
类型实例
行?我还没有学习类型族:还有,为什么我们需要不可判定的实例?最后,为什么我既需要类型族
缩放
又需要缩放实例?我很想了解为什么这一切都有效,为什么这一切都是必需的。干杯!:)@Chris Penner类型族在类型级别上有点像函数。它们接受一种类型(在我们的例子中是
buaction
),并生成另一种类型(在我们的例子中是
Zoomed(StateT Buffer IO)
)。与常规函数相比,它们有一些特殊性:语法不同,不同的“案例”可以分散在不同的模块中,等等。您可以使用
:kind在
ghci
中“运行”类型族命令,比如
:种类!缩放(StateT Buffer IO)
@Chris Penner
不可判定实例
是必需的,因为在处理复杂实例定义时,GHC可能无法确定类型检查是否可以终止。打开
UndedicatableInstances
会说“编译器,不要担心没有终止的类型检查,如果需要太长时间,我只需按Ctrl-C键就可以了。”当然,如果类型检查真的终止,这不是一个有害的扩展。这非常有效!我很感谢@danidiaz的解释,当然,很高兴终于了解了类型族!但是有什么理由我应该使用类型族向导而不是此解决方案吗?是的,如果您想添加使用
zoom
或类似
zoombulationtoalteration
之类的功能,那么我应该使用类型族向导。如果你有很多不同的可缩放的东西,这个原因可能(如@danidiaz所写)。
bufDo :: BufAction () -> Alteration ()
bufDo = Alteration . zoom (editor . buffers . traverse) . runBufAction