Coroutine (Await Int) (State MyState) Int 的缩放实例

Zoom instance for Coroutine (Await Int) (State MyState) Int

我有一个深度嵌套的状态 MyState,它在 Coroutine 中使用(来自 monad-coroutine 包):

Coroutine (Await Int) (State MyState) Int

而且我喜欢使用缩放(来自 lens)进行一些操作的可能性,因为它允许我写类似

的东西
zoom (company.assets) $ do
   a.b = 3
   c.d = 'good'
   ...

不幸的是,我无法为我拥有的 Coroutine 提出正确的 instance Zoom 声明。

我的尝试是这样的:

instance Zoom (Coroutine z s) (Coroutine z t) s t where
    zoom l (Coroutine m) = undefined

但此时 GHC 已经告诉我

Expecting one more argument to ‘s’
The third argument of ‘Zoom’ should have kind ‘*’,
  but ‘s’ has kind ‘* -> *’
In the instance declaration for
  ‘Zoom (Coroutine z s) (Coroutine z t) s t’

但是如果我做类似

instance Zoom (Coroutine z s) (Coroutine z t) (s a) (t a) where
    zoom l (Coroutine m) = undefined

然后 GHC 抱怨:

Illegal instance declaration for
  ‘Zoom (Coroutine z s) (Coroutine z t) (s a) (t a)’
  The liberal coverage condition fails in class ‘Zoom’
    for functional dependency: ‘m -> s’
  Reason: lhs type ‘Coroutine z s’ does not determine rhs type ‘s a’
In the instance declaration for
  ‘Zoom (Coroutine z s) (Coroutine z t) (s a) (t a)’

现在我明白了,我是在盲目地做某事,而不了解对我的实际期望。任何人都可以解释 Zoom 实例是如何为我的 Coroutine 定义的,以及为什么定义它(因此不仅对正确的声明感兴趣,而且对构造此类声明时的思考过程也有一些注意事项)?

错误

Expecting one more argument to ‘s’
The third argument of ‘Zoom’ should have kind ‘*’,
  but ‘s’ has kind ‘* -> *’
In the instance declaration for
  ‘Zoom (Coroutine z s) (Coroutine z t) s t’

GHC 在这里抱怨类型不匹配

  ‘Zoom (Coroutine z s) (Coroutine z t) s t’
                     ^                  ^

Coroutine的第二个类型参数应该是monad(* -> *),而s是state的类型(*)作为第三个类型Zoom 的参数。实例声明应该是

instance (Functor f, Zoom m n s t) => Zoom (Coroutine f m) (Coroutine f n) s t

连同一些样板文件以满足 Zoom 的上下文:

{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE UndecidableInstances  #-}

instance (Functor f, MonadState s m) => MonadState s (Coroutine f m) where
  get = lift get
  put = lift . put

type instance Zoomed (Coroutine s m) = Zoomed m

经过几个小时的检查,我意识到我上面建议的是不可能实现的,zoom 的类型太受限制,无法与 mapMonad 一起使用,我不知道办法克服它。然而,如果一切都是单态的,它仍然有效:

data MyState = MyState
  { _myRecord :: String }

$(makeLenses ''MyState)

zoomMyState :: Functor f => Coroutine f (State String) r -> Coroutine f (State MyState) r
zoomMyState = mapMonad (zoom myRecord)