haskell - 混合 MonadIO、MonadState 和缩放时出现“无法推断”错误
问题描述
我正在做一个简单的蒙特卡罗模拟,我遇到了这个问题。我使用MonadIO
,MonadState
和MonadRandom
来简化程序状态的维护。我遇到了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
解决方案
Zoom
通常不会让您以多态方式工作MonadState
,因为MonadState
单独并不能提供一种交换状态类型的方法。
也就是说,如果你想使用Zoom
,你可能应该使用具体的StateT
,如果你不需要StateT
特别多态性并且只能在底层单子(即m
in 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 ()
是 aFunctor
,类型族应用程序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)
推荐阅读
- xcode - Xcode - 如果单击文件,则查找现有选项卡而不是始终打开新选项卡
- java - 打印到 PDF 文件 JavaFX
- python - OSM 版本的 gmplot
- unit-testing - Mocha 文件执行顺序和异步代码
- r - 复制矩阵的列而不更改行
- android - 所有 com.android.support 库必须使用相同版本的 appcompat-v7:28.0.0
- reactjs - React-Day-Picker 弹出位置
- gams-math - GAMS - 单位阶跃函数
- angular - Angular 6 + Laravel 5.6:httpClient.post 的 CORS 问题
- python - 根据烧瓶 SQLAlchemy 中的 func.sum() 值将结果传递给模板