在下面的例子中,我在內部實作了函式f。它的簽名a就像型別變數a是有范圍的一樣使用,即使我沒有啟用擴展,它也不會給我一個編譯錯誤ScopedTypeVariables:
foo :: Int -> [a] -> [a]
foo n list = snd $ foldl f (1,[]) list where
f :: (Int,[a]) -> a -> (Int,[a])
f (k,l) r
| k==n = (1,l [r])
| otherwise = (k 1,l)
我期待錯誤:"Couldn't match type ‘a1’ with ‘a’ ...."因為,正如我所說,我將型別變數a視為有范圍的,即使它沒有范圍。
我知道什么是剛性型別,并且我已經知道為什么會出現錯誤。我的問題是為什么我在這里沒有收到錯誤訊息。
uj5u.com熱心網友回復:
功能
f :: (Int,[a]) -> a -> (Int,[a])
f (k,l) r
| k==n = (1,l [r])
| otherwise = (k 1,l)
一般足以自行鍵入檢查(假設n::Int)。a這里的型別確實可以是任何型別——無需假設它a與a的簽名中的型別相同foo,因此不會產生錯誤。
換句話說,我們可以毫無傷害地重命名a為b:
foo :: Int -> [a] -> [a]
foo n list = snd $ foldl f (1,[]) list where
f :: (Int,[b]) -> b -> (Int,[b])
f (k,l) r
| k==n = (1,l [r])
| otherwise = (k 1,l)
這是一個更簡單的例子來強調這一點。這編譯沒有錯誤:
foo :: a -> [a]
foo x = [f x]
where
f :: a -> a -- this could also be b->b
f y = y
相比之下,將最后一行更改為f y = x會立即觸發錯誤,因為x型別是“a簽名中的foo”,而不是“a簽名中的f”,并且兩個型別變數無法統一,因為它們都是剛性的。
uj5u.com熱心網友回復:
foo :: Int -> [a] -> [a]
foo n list = snd $ foldl f (1,[]) list where
f :: (Int,[a]) -> a -> (Int,[a])
f (k,l) r
| k==n = (1,l [r])
| otherwise = (k 1,l)
請注意您的內部函式如何f僅使用n外部函式的作用域。n是型別Int; 只有list(和foo自身)的名字,其型別有什么用a的型別變數foo。由于f不使用這些,它不需要具有ScopedTypeVariables用于a從foo.
在effet,f是對自己完全多型的; 它可以使用自己的a實體化為任何型別。因為它只在一個地方(內部foo)使用,所以它實際上只會a在a從外部實體化到 的情況下使用foo。但是,在 Haskell 中實體化任何多型型別的程序完全相同;定義f具有治療a作為一個黑盒子和作業與任何可能的型別,并且住的地方f是使用可以在任何實體化它(在這種情況下,a從型別foo)。
讓我們通過使用顯式 foralls 來更明確地展示這一點,而不是為不同的型別變數賦予相同的名稱:
foo :: forall a. Int -> [a] -> [a]
foo n list = snd $ foldl f (1,[]) list where
f :: forall b. (Int,[b]) -> b -> (Int,[b])
f (k,l) r
| k==n = (1,l [r])
| otherwise = (k 1,l)
fworks forall b,并且它只是一個想要實體化的特定用法的巧合b ~ a(即使“一個特定用法”實際上是唯一的用法)。
嘗試撰寫區域函式型別(需要ScopedTypeVariables決議)時的典型錯誤是當內部函式使用外部函式中的某些內容時,該外部函式在其型別中具有變數。說,像這樣:
bar :: (a -> Bool) -> [a] -> [a]
bar f xs = filter g xs
where g :: a -> Bool
g = not . f
乍一看這可能看起來不錯,但 for 的型別簽名g實際上是一個謊言,因為沒有ScopedTypeVariables它意味著:
bar :: forall a. (a -> Bool) -> [a] -> [a]
bar f xs = filter g xs
where g :: forall b. b -> Bool
g = not . f
它聲稱g可以接受任何型別的東西并將其轉換為 a Bool,而實際上g使用的是 function f。f根本不適用于任何型別,僅適用bar于呼叫的特定型別。bar可以采取一個函式f的任何型別(主叫方bar可以選擇以實體a為任何型別),但g運行“一個特定的呼叫里面bar”的時候,我們已經通過了一個特別的f。
要正確撰寫這種型別,我們需要(使用ScopedTypeVariables):
bar :: forall a. (a -> Bool) -> [a] -> [a]
bar f xs = filter g xs
where g :: a -> Bool
g = not . f
uj5u.com熱心網友回復:
沒有錯誤,因為您的內部函式沒有從外部函式參考任何型別變數。它使用n但它的型別是普通的Int,而不是多型的
當我們將 maken的型別設為多型時,會發生“剛性型別變數”錯誤:
foo :: Num a => a -> [a] -> [a]
foo n list = snd $ foldl f (1,[]) list where
f :: Num a => (a,[a]) -> a -> (a,[a])
f (k,l) r
| k==n = (1,l [r])
| otherwise = (k 1,l)
由于所有型別簽名都forall在它們前面添加了一個隱式簽名,因此 for 的型別簽名也是如此f。每個都forall定義了自己的范圍。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/389042.html
