簡體   English   中英

為什么這個功能無法進行類型檢查?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM