如何放大monad变压器?

时间:2016-12-13 20:44:00

标签: haskell lens state-monad

再次感谢您的帮助!

我大量使用E. Kmett的镜头库,以避免X / Y问题我会解释一些背景。

我正在使用可扩展的文本编辑器,并希望为扩展编写器提供monad DSL,Alteration是一个monad转换器堆栈,其状态为Store类型的状态,基本上存储整个文本编辑器。 Store内的Editor内有Buffer个。用户可以指定Alteration来对整个商店进行操作,但为了简化操作,我还提供了BufAction,它只能在一个缓冲区上运行。

我计划通过使用名为bufDo的帮助程序来实现此功能,该帮助程序在BufAction上运行Buffer,并运行focusDo BufAction关注焦点' 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)

以下是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实例(如果是这样的话),我也会喜欢它。

干杯!

2 个答案:

答案 0 :(得分:3)

除了答案@danidiaz。

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

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

答案 1 :(得分:2)

似乎有一个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 原始状态,< em>缩小状态:

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)       

但是如果你有很多不同的可缩放的东西,记住每个缩放功能的名称可能是一件苦差事。这是类型类可以帮助的地方。