[英]Why does this function fail to typecheck?
在關於函數式編程的講座中,我們看到了以下Haskell函數:
f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z x) + (z y)
預計此功能將無法進行類型檢查。 但是,沒有解釋這種情況發生的原因。 在GHCI中嘗試時,我得到以下輸出:
Prelude> :l test [1 of 1] Compiling Main ( test.hs, interpreted ) test.hs:2:35: Couldn't match expected type `a' with actual type `Bool' `a' is a rigid type variable bound by the type signature for f :: Bool -> Int -> (a -> Int) -> Int at test.hs:1:6 Relevant bindings include z :: a -> Int (bound at test.hs:2:7) f :: Bool -> Int -> (a -> Int) -> Int (bound at test.hs:2:1) In the first argument of `z', namely `x' In the first argument of `(+)', namely `(z x)' Failed, modules loaded: none.
為什么會這樣?
f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z x) + (z y)
類型簽名斷言我們的函數z
在其第一個參數中是多態的。 它接受任何類型的值a
並返回Int
。 然而,類型變量的范圍界定a
也意味着它必須是同一類型a
在所有用途。 a
不能在同一個使用站點實例化為不同類型。 這是“等級1多態性”。
您可以真正閱讀該類型:
f :: forall a. Bool -> Int -> (a -> Int) -> Int
所以:
z (x :: Bool) + z (y :: Int)
無效,因為a
被約束為兩種不同的獨立類型。
語言擴展使我們能夠改變的范圍a
,以便它可以被實例化多態變量-即在相同的使用部位來容納不同類型,包括具有多態函數類型:
Prelude> :set -XRankNTypes
Prelude> let f :: Bool -> Int -> (forall a . a -> Int) -> Int
f x y z = if x then y + y else (z x) + (z y)
現在類型a
沒有全局范圍,並且各個實例可以變化。 這讓我們可以編寫“更多態”函數f
並使用它:
Prelude> f True 7 (const 1)
14
這就是更高等級的多態性是多么酷。 更多代碼重用。
這根本不是簡單的參數多態如何工作。 函數z
在函數的簽名中是多態的,但在體內它只是單態的。
在對定義進行類型檢查時,類型檢查器會推斷出類型變量a
的單形類型,以便在函數的定義中使用。 但是你的f
嘗試用兩種不同的類型調用z
,因此類型檢查器會為a
推斷兩種沖突類型。
甚至
f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z y) + (z y)
不會進行類型檢查(正如在注釋中指出的那樣),並生成相同的錯誤,因為Haskell推斷出表達式的最不常規類型 ,並且您的類型比推斷的類型更通用。 正如“Haskell的溫和介紹”所說的那樣,
表達式或函數的主體類型是最不通用的類型,直觀地說,“包含表達式的所有實例”。
如果您明確指定了一個類型,Haskell假定您出於某種原因這樣做,並堅持將推斷類型與給定類型相匹配。
對於上面的表達式,推斷類型是(Num t) => Bool -> t -> (t -> t) -> t
,所以當匹配類型時,它會看到你為y
賦予了Int
,而z
的類型變成了(Int -> Int)
。 這是小於普通(a -> Int)
。 但你堅持要有a
(不是Int
) - 一個剛性的變量。 換句話說,你的函數f
只能接受Int -> Int
類型的函數,但你堅持它可以給它任何函數:: a -> Int
,包括:: String -> Int
等(作為@augustsson點在評論中)。 您聲明的類型太寬泛了。
類似地,如果你只有一個(zx)
,它將匹配給定的x
類型,並且比z
函數的聲明類型(Bool -> Int)
更窄。 然而又抱怨剛性類型變量。
實際上,你聲明了類型(Num t) => Bool -> t -> (t1 -> t) -> t
但它確實是(Num t) => Bool -> t -> (t -> t) -> t
。 這是一種不同的類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.