[英]Haskell - Ambiguous type variable
我在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
然后,當我嘗試在ghci中運行以下命令時:
runStateT getNextState 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)
...
我以為是在抱怨我的getNextState函數的默認實現未指定具體類型,因此我嘗試了以下操作:
getNextState :: GameState b => GameStateMonad a (Maybe b)
getNextState = return (Nothing :: Maybe Blank)
不幸的是,我在編譯時遇到此錯誤:
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
...
但是我發現,當我調用getNext狀態時添加類型簽名可以使代碼運行:
runStateT (getNextState :: GameStateMonad Blank (Maybe Blank)) Blank
不幸的是,這阻止了我編寫通用代碼來處理游戲狀態。 這對我也沒有意義。 如果必須在返回多態類型后將其指定為顯式類型,那么返回它有什么意義呢? 最初的問題也讓我感到困惑,因為我可以執行以下功能:
test :: Num a => Maybe a
test = Nothing
並且沒有問題運行它。 這不應該抱怨像我的原始代碼這樣的模棱兩可的類型嗎? 同樣,當給返回值一個顯式類型時,我不能像以前那樣編譯它:
test :: Num a => Maybe a
test = Nothing :: Maybe Int
我不明白為什么這是一個問題。 Int是Num類型的實例,因此函數的類型正確。
我有四個問題:
為什么在返回類型類的元素時給出顯式類型會導致編譯錯誤?
為什么在getNextState內部返回模棱兩可的Maybe值會導致錯誤,但在測試內部卻不會導致錯誤?
為什么會出現沒有我調用返回的多態數據的函數這個錯誤,因為解釋在這里 ?
在上面的鏈接中 ,答案提到“ [[您得到此錯誤],因為您有產生多態結果的東西,然后對該結果應用一個接受多態參數的函數,從而使中間值的類型未知”。 這不是意味着返回多態結果的函數實際上是沒有用的嗎?
謝謝。
Cat Plus Plus已經解釋了原因
getNextState :: GameState b => GameStateMonad a (Maybe b)
getNextState = return (Nothing :: Maybe Blank)
不起作用,因此我可能會對此有所欠缺。 類型簽名保證getNextState
可以為調用者要求的任何類型b
傳遞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)
從打字
runStateT getNextState Blank
在ghci提示符下。 如果您問ghci類型,它將告訴您
runStateT getNextState Blank :: GameState b => IO (Maybe b)
(不保證選擇類型變量)。 但是沒有上下文,因此ghci不知道實例化b
類型。 因此,它不知道應該調用getNextState
哪個實現[或者,如果我們查看GHC的類型類的實現,則應該傳遞哪個字典]。 它無法解決這種歧義,因此它可以告訴您並建議您如何解決。
test :: Num a => Maybe a
test = Nothing
是的,當您在ghci提示符下鍵入test
時,原則上是相同的問題。 但是,當涉及到一個數字類時(其中歧義最常見,文字已經很歧義),並且涉及的所有約束都很簡單,並且涉及Prelude或標准庫的類,則有解決特殊類型變量的特殊規則 。 在這種情況下,默認情況下會實例化不明確的類型變量,因此ghci將選擇使用Integer
實例化a
並打印Maybe Integer
類型的Nothing
。
您的GameState
類不是默認值,這是這些示例之間的區別。
為什么會出現沒有我調用返回的多態數據的函數這個錯誤,因為解釋在這里 ?
因為您沒有調用任何確定類型的函數。 如果您具有類型的功能
foo :: Blank -> Int
並輸入
runStateT getNextState Blank >>= print . maybe 0 foo
使用foo
將決定b
並且一切都會膨脹。
但是,如果調用多態函數(無法從其結果類型推斷出其參數類型),則該問題將無法解決,但會更加惡化,如鏈接示例中所示。 這樣,模棱兩可的類型就不再可以從外部訪問,並且永遠無法解決。 那么唯一的方法是提供一種解決歧義的類型簽名。
這不是意味着返回多態結果的函數實際上是沒有用的嗎?
哦,不,它們非常有用。 看一下read
,或者fromInteger
, realToFrac
,...
關鍵是,必須以某種方式確定在何處使用它們。 大部分時間是由調用上下文完成的,但有時需要顯式的類型簽名。
我不確定將GameState
設為類型類要達到什么目的。 您在這里的OOP思維定式可能太多了-類型類不是OOP類。 一組游戲狀態可能會關閉,因此僅使其成為一個ADT可能更有意義。
為什么在返回類型類的元素時給出顯式類型會導致編譯錯誤?
查看函數的簽名: GameState b => GameStateMonad a (Maybe b)
。 現在請記住,這意味着forall b. GameState b => GameStateMonad a (Maybe b)
forall b. GameState b => GameStateMonad a (Maybe b)
。
函數的實現無法確定b
是什么-它必須對所有 b
都起作用(只要它們適合約束),因為調用者可以自行決定。
這就是為什么return (Nothing :: Maybe Blank)
是錯誤的原因-不適用於所有類型b
僅適用於Blank
。 “無法推斷(b ~ Blank)
b〜 (b ~ Blank)
”表示GHC在這里不能證明類型相等。 同樣的事情也Nothing :: Maybe Int
。 這就是為什么在呼叫站點中添加類型簽名的原因。
也許以后我會寫一些歧義。 我仍然非常確定您無論如何都要過度設計此代碼,因此解決方案是不要這樣做。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.