如何将缩放与新类型的 MonadState 约束一起使用?

How do I use zoom with a MonadState constraint of a newtype?

我有两个函数,一个具有包装类型的 MonadState 约束,另一个具有未包装类型的 MonadState 约束。我想无缝 运行 第一个功能中的第二个功能。

例如:

import qualified Control.Monad.State.Strict as MTL
import Data.Monoid (Sum)
import Control.Lens.Zoom (zoom)
import Control.Lens.Wrapped (_Wrapped')

outer :: (MTL.MonadState (Sum Int) m) => m Int
outer = zoom _Wrapped' inner

inner :: (MTL.MonadState Int m) => m Int
inner = MTL.get

以上在我看来应该可行,但我收到 3 个类型检查器错误。第一个:

Could not deduce (Control.Lens.Zoom.Zoom m0 m Int (Sum Int))
  arising from a use of ‘zoom’
from the context (MTL.MonadState (Sum Int) m)
  bound by the type signature for
             outer :: MTL.MonadState (Sum Int) m => m Int
The type variable ‘m0’ is ambiguous

根据我的搜索,我的印象是 zoom 不能做我想做的事。 (在 http://ircbrowse.net/browse/haskell "reltuk: yeah, thats the unfortunate downside of lenses is that zooming forces you to a concrete state monad" 上找到了这句话)我猜这与错误消息一致,指出 "m0 is ambiguous".

我真的不想将我的 MonadState 约束更改为具体的 monad。

有没有其他标准方法可以做我想做的事?

编辑:

这不会进行类型检查:

sumOuter :: (Functor (Zoomed m Int),
           Zoom m m Int t,
           Wrapped t,
           Unwrapped t ~ Int,
           MTL.MonadState (Sum Int) m) => m Int
sumOuter = zoom _Wrapped' sumInner

sumInner :: (MTL.MonadState Int m) => m Int
sumInner = MTL.get

zoom 有自己的 class 用于重载,所以难怪 MonadState 不削减它。 Zoom class 涵盖与 mtl 大致相同的领域,尽管它的机制稍微复杂一些。在任何情况下,您都没有义务使用具体的 monad 进行编程。

您可以尝试启用 NoMonomorphismRestriction 并推断类型:

{-# LANGUAGE NoMonomorphismRestriction #-}

import Control.Lens.Internal.Zoom
import Control.Lens
import Control.Monad.State
import Data.Monoid
import Control.Monad.Except -- for illustration

outer = zoom _Wrapped' get

现在:t outer

outer :: 
  (Functor (Zoomed m (Unwrapped t)), Zoom m n (Unwrapped t) t, Wrapped t) 
  => n (Unwrapped t)

这不是很漂亮,但它似乎有效。

> runState outer (Sum 10)
(10, Sum {getSum = 10})
> runState (runExceptT outer) (Sum 10) :: (Either String Int, Sum Int)
(Right 10,Sum {getSum = 10})

编辑:

如果你真的想特化到Sum Int作为外层状态,又想有MonadState (Sum Int) n约束,这就够了:

sumOuter ::
  (Functor (Zoomed m Int), Zoom m n Int (Sum Int)) => n Int
sumOuter = zoom _Wrapped' sumInner

sumInner :: (MTL.MonadState Int m) => m Int
sumInner = MTL.get

MonadState 约束呢?没必要写出来,因为 Zoom m n s tMonadState s mMonadState t n 作为 superclass 。

同样,更一般的 outer 函数已经暗示了 MonadState t n