我遇到了Haskell中模糊类型的问题.我从以下开始:
module GameState
( GameState(..)
, GameStateMonad
, module Control.Monad.Trans
, module Control.Monad.Trans.State.Lazy
, Blank(..)
) where
import Control.Monad.Trans
import Control.Monad.Trans.State.Lazy
type GameStateMonad a b = StateT a IO b
class GameState a where
update :: Double -> GameStateMonad a ()
update deltaTime = return ()
draw :: GameStateMonad a ()
draw = return ()
getNextState :: GameState b => GameStateMonad a (Maybe b)
getNextState = return Nothing
isStateFinished :: GameStateMonad a Bool
isStateFinished = return True
-- This is just a dummy data and instance declaration to demonstrate the error
data Blank = Blank
instance GameState Blank
Run Code Online (Sandbox Code Playgroud)
然后当我尝试在ghci中运行以下内容时:
runStateT getNextState Blank
Run Code Online (Sandbox Code Playgroud)
我明白了:
Ambiguous type variable `b0' in the constraint:
(GameState b0) arising from a use of `getNextState'
Probable fix: add a type signature that fixes these type variable(s)
...
Run Code Online (Sandbox Code Playgroud)
我以为它抱怨我的getNextState函数的默认实现没有指定具体类型,所以我尝试了以下内容:
getNextState :: GameState b => GameStateMonad a (Maybe b)
getNextState = return (Nothing :: Maybe Blank)
Run Code Online (Sandbox Code Playgroud)
不幸的是我在编译时遇到了这个错误:
Could not deduce (b ~ Blank)
from the context (GameState a)
bound by the class declaration for `GameState'
at GameState.hs:(14,1)-(25,33)
or from (GameState b)
bound by the type signature for
getNextState :: GameState b => GameStateMonad a (Maybe b)
at GameState.hs:22:5-50
`b' is a rigid type variable bound by
the type signature for
getNextState :: GameState b => GameStateMonad a (Maybe b)
at GameState.hs:22:5
...
Run Code Online (Sandbox Code Playgroud)
但我发现在调用getNext状态时添加类型签名允许代码运行:
runStateT (getNextState :: GameStateMonad Blank (Maybe Blank)) Blank
Run Code Online (Sandbox Code Playgroud)
不幸的是,这阻止我制作处理游戏状态的通用代码.这对我来说也没什么意义.如果在返回后必须给它一个显式类型,那么返回多态类型有什么意义呢?最初的问题对我来说也很困惑,因为我可以做如下函数:
test :: Num a => Maybe a
test = Nothing
Run Code Online (Sandbox Code Playgroud)
运行它没有问题.这不应该像我的原始代码那样抱怨含糊不清的类型吗?另外,当给返回值一个显式类型时,我无法编译它,就像之前一样:
test :: Num a => Maybe a
test = Nothing :: Maybe Int
Run Code Online (Sandbox Code Playgroud)
我不明白为什么这是一个问题.Int是Num类型的实例,因此函数的类型是正确的.
我有四个问题:
为什么在返回类型类的元素时给出显式类型会导致编译错误?
为什么在getNextState中返回一个模糊的Maybe值会导致错误,但在测试中却没有?
为什么会出现没有我调用返回的多态数据的函数这个错误,因为解释在这里?
在上面的链接中,答案提到"[你得到这个错误],因为你有一些产生多态结果的东西,然后应用一个函数,该函数对该结果采用多态参数,这样中间值的类型是未知的".这是否意味着返回多态结果的函数基本上没用?
谢谢.
Cat Plus Plus已经解释了原因
getNextState :: GameState b => GameStateMonad a (Maybe b)
getNextState = return (Nothing :: Maybe Blank)
Run Code Online (Sandbox Code Playgroud)
不起作用,所以我可以做到这一点.类型签名承诺getNextState可以Maybe b为调用者要求的任何类型b提供类型的值.如果函数具有多态返回类型,则函数的调用者决定它将返回什么类型.因此,签名承诺"无论你想要什么,只要它是一个GameState实例",但实施说"不,我不关心你订购什么,我回来了Blank".
Ambiguous type variable `b0' in the constraint:
(GameState b0) arising from a use of `getNextState'
Probable fix: add a type signature that fixes these type variable(s)
Run Code Online (Sandbox Code Playgroud)
从打字
runStateT getNextState Blank
Run Code Online (Sandbox Code Playgroud)
在ghci提示.如果你问ghci的类型,它会告诉你
runStateT getNextState Blank :: GameState b => IO (Maybe b)
Run Code Online (Sandbox Code Playgroud)
(不保证选择类型变量).但是没有上下文,所以ghci不知道要实例化哪种类型b.所以它不知道它应该调用哪个实现getNextState[或者,如果我们看一下GHC的类型类实现,它应该通过哪个字典].它无法解决这种模糊性,所以它会告诉你它并建议你如何解决它.
test :: Num a => Maybe a
test = Nothing
Run Code Online (Sandbox Code Playgroud)
是的,当您test在ghci提示符下键入时,原则上也是同样的问题.但是,当涉及一个数字类时(其中歧义最常见,文字已经不明确),并且所有涉及的约束都很简单并涉及Prelude或标准库的类,有一些特殊规则可用于解决模糊类型变量.在这种情况下,不明确的类型变量被默认实例,所以ghci中会选择实例a与Integer和打印Nothing的类型Maybe Integer.
你的GameState课程不是违约的,这就是这些例子之间的区别.
为什么会出现没有我调用返回的多态数据的函数这个错误,因为解释在这里?
因为您没有调用任何可以确定类型的函数.如果你有类型的功能
foo :: Blank -> Int
Run Code Online (Sandbox Code Playgroud)
并键入
runStateT getNextState Blank >>= print . maybe 0 foo
Run Code Online (Sandbox Code Playgroud)
使用foo会决定b,所有都会膨胀.
但是,如果您调用多态函数(其参数类型无法从其结果类型推断),则问题将无法解决,但会加剧,如链接示例中所示.然后,不再可以从外部访问模糊类型,并且永远无法解决它.然后唯一的方法是提供一个解决模糊性的类型签名.
这是否意味着返回多态结果的函数基本上没用?
哦不,他们非常有用.看看read,或者fromInteger,realToFrac...
关键是,它们的使用类型必须以某种方式确定它们的使用位置.大多数时间由调用上下文完成,但有时需要显式类型签名.