首页 > 解决方案 > 混合 MonadIO、MonadState 和缩放时出现“无法推断”错误

问题描述

我正在做一个简单的蒙特卡罗模拟,我遇到了这个问题。我使用MonadIO,MonadStateMonadRandom来简化程序状态的维护。我遇到了Count not deduce错误,但是当我删除程序的顶级 monad 的类型时,它突然编译得很好。

我对这种类型做错了吗?我需要指定更严格的类型吗?你能解释一下为什么添加类型信息会导致类型计算更模糊而不是更模糊吗?

更新

我尝试在 ghci 中使用 :t 来获取类型,然后我得到了(Field1 s s Int Int, Zoom m n Int s, Functor (Zoomed m ())). 我在原来的程序中做了同样的事情并得到了(Zoom m1 m (Map Int Int) ProgramState, Functor (Zoomed m1 ()), MonadRandom m1, MonadIO m)

我很高兴在这里有一些东西,但是由于放入类型的主要目的是使代码更具可读性并使错误消息更好,我想了解如何生成和读取这些类型。

我还想更好地了解添加类型约束如何导致类型计算变得模棱两可。

结束更新

我已经简化了代码:

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State.Strict
import Control.Lens

updateCount :: (MonadState Int a) => a ()
updateCount = modify (+ 1)

run :: MonadState (Int, Int) a => a ()
run = zoom _1 updateCount

main = execStateT run (0, 0) >>= print

如果我删除该run ::行,一切正常,但如果我尝试按原样编译,我会收到以下错误:

[1 of 1] Compiling Main             ( bug-report.hs, bug-report.o )

bug-report.hs:9:7: error:
    • Could not deduce (Zoom m0 a Int (Int, Int))
        arising from a use of ‘zoom’
      from the context: MonadState (Int, Int) a
        bound by the type signature for:
                   run :: MonadState (Int, Int) a => a ()
        at bug-report.hs:8:1-38
      The type variable ‘m0’ is ambiguous
      Relevant bindings include run :: a () (bound at bug-report.hs:9:1)
      These potential instances exist:
        instance Monad z => Zoom (StateT s z) (StateT t z) s t
          -- Defined in ‘Control.Lens.Zoom’
        ...plus 12 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: zoom _1 updateCount
      In an equation for ‘run’: run = zoom _1 updateCount

bug-report.hs:9:12: error:
    • Could not deduce (Functor (Zoomed m0 ()))
        arising from a use of ‘_1’
      from the context: MonadState (Int, Int) a
        bound by the type signature for:
                   run :: MonadState (Int, Int) a => a ()
        at bug-report.hs:8:1-38
      The type variable ‘m0’ is ambiguous
      These potential instances exist:
        instance Functor Identity -- Defined in ‘Data.Functor.Identity’
        instance Functor IO -- Defined in ‘GHC.Base’
        instance [safe] Functor m => Functor (StateT s m)
          -- Defined in ‘Control.Monad.Trans.State.Strict’
        ...plus four others
        ...plus 41 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the first argument of ‘zoom’, namely ‘_1’
      In the expression: zoom _1 updateCount
      In an equation for ‘run’: run = zoom _1 updateCount

bug-report.hs:9:15: error:
    • Could not deduce (MonadState Int m0)
        arising from a use of ‘updateCount’
      from the context: MonadState (Int, Int) a
        bound by the type signature for:
                   run :: MonadState (Int, Int) a => a ()
        at bug-report.hs:8:1-38
      The type variable ‘m0’ is ambiguous
      These potential instances exist:
        instance [safe] Monad m => MonadState s (StateT s m)
          -- Defined in ‘Control.Monad.State.Class’
        ...plus 13 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the second argument of ‘zoom’, namely ‘updateCount’
      In the expression: zoom _1 updateCount
      In an equation for ‘run’: run = zoom _1 updateCount

标签: haskellhaskell-lens

解决方案


Zoom通常不会让您以多态方式工作MonadState 因为MonadState单独并不能提供一种交换状态类型的方法。

也就是说,如果你想使用Zoom,你可能应该使用具体的StateT,如果你不需要StateT特别多态性并且只能在底层单子(即min StateT s m)上摆脱多态性。或者,您可以跳过zoom并仍然使用镜头来关注纯函数的状态部分,使用诸如modifying—here modifying _1 (+ 1)、.

当您删除 的类型签名时run,推断的类型(带有NoMonomorphismRestriction)是这样的:

run ::
  ( Zoom m n Int s
  , Field1 s s Int Int
  , Functor (Zoomed m ())
  )
  => n ()

那是:

  • 某个外部monad中的动作,n其状态为s(here, (Int, Int))

  • 在某个内部monad中包装一个动作,m其状态为Int

  • 您可以在哪里查看和更新Int​​第一个索引s

  • 并且Zoomed m ()是 a Functor,类型族应用程序Zoomed (StateT s u) ()评估为Focusing u (); 由于Functor镜头类型的限制,这是必需的,Functor f => (a -> f b) -> (s -> f t)

此处的Functor约束是必要的,因为在类型族Zoomed应用于具体的m. 但是请注意,您不需要MonadState约束,因为它是由Zoom.

根据您的特定用例,您可以通过几种不同的方式稍微简化这一点。例如,使用具体的外部状态:

run ::
  ( Zoom m n Int (Int, Int)
  , Functor (Zoomed m ())
  )
  => n ()

或者将责任传递给调用者并采取Lens

runOn
  :: (Zoom m n Int s, Functor (Zoomed m ()))
  => LensLike' (Zoomed m ()) s Int
  -> n ()
runOn lens = zoom lens updateCount
print =<< execStateT (runOn _1) (0 :: Int, 0 :: Int)

推荐阅读