在從第一原理學習 Haskell 和Haskell 編程時,發現了一個讓我困惑的練習。
這是簡短的版本:
For the following definition:
a) i :: Num a => a
i = 1
b) Try replacing the type signature with the following:
i :: a
替換給了我一個錯誤:
error:
? No instance for (Num a) arising from the literal ‘1’
Possible fix:
add (Num a) to the context of
the type signature for:
i' :: forall a. a
? In the expression: 1
In an equation for ‘i'’: i' = 1
|
38 | i' = 1
| ^
我或多或少清楚 Num 約束是如何產生的。不清楚為什么分配1給多型變數i'會產生錯誤。
為什么這樣有效:
id 1
而這個沒有:
i' :: a
i' = 1
id i'
如果沒有問題,是否可以將更具體的值分配給不太具體的值并丟失一些型別資訊?
uj5u.com熱心網友回復:
這是一個常見的誤解。您可能有一些想法,例如,在類 OO 語言中,
class Object {};
class Num: Object { public: Num add(...){...} };
class Int: Num { int i; ... };
然后,您將能夠將Int值用作需要引數的函式的Num引數,或者將Num值用作需要Object.
但這根本不是 Haskell 的型別類的作業方式。Num不是一類值(例如,在上面的示例中,它將是屬于某個子類的所有值的類)。相反,它是代表特定數字風格的所有型別的類。
那有什么不同?好吧,多型文字1 :: Num a => a不會生成特定Num值,然后可以向上轉換為更通用的類。相反,它希望呼叫者首先選擇要在其中呈現數字的具體型別,然后立即以該型別生成數字,之后該型別永遠不會改變。
換句話說,多型值具有隱式型別級別的引數。誰想使用都i需要在兩者都使用的環境中使用
a應該使用什么型別是明確的。(它不一定需要在那里修復:呼叫者本身也可以是多型函式。)- 編譯器可以證明這個型別
a有一個Num實體。
在 C 中,Haskell 型別類/多型文字的類似物不是 [子] 類及其物件,而是受限于概念的模板:
#include <concepts>
template<typename A>
concept Num = std::constructible_from<A, int>; // simplified
template<Num A>
A poly_1() {
return 1;
}
現在,poly_1可以在需要滿足該Num概念的型別的任何設定中使用,即特別是型別是constructible_froman int,但不能在需要其他型別的背景關系中使用。
(在較舊的 C 中,這樣的模板只是鴨子型別,即它并不明確需要Num設定,但編譯器會嘗試按原樣使用它,然后在注意到1無法轉換為時給出型別錯誤指定型別。)
uj5u.com熱心網友回復:
tl;博士
i'宣告為的值i' :: a必須可用1代替任何其他值,無一例外。1沒有這樣的值,因為它不能被使用,比如說,在String預期 a 的地方,只是舉一個例子。
更長的版本
讓我們從一個沒有爭議的場景開始,你確實需要一個型別約束:
plus :: a -> a -> a
plus x y = x y
這不會編譯,因為簽名等價于plus :: forall a. a -> a -> a,而且 RHS 顯然不正確,x y對任何常見的型別都是有意義的a并且x是y其中的居民。因此,您可以通過提供一個保證 在兩個 s 之間可能存在的約束來解決上述問題a,并且您可以通過在Num a =>之后放置::(或者甚至放棄多型型別而只是更改a為Int)來做到這一點。
但是有些函式不需要對其引數進行任何限制。這是其中的三個:
id :: a -> a
id x = x
const :: a -> b -> a
const x _ = x
Data.Tuple.swap :: (a, b) -> (b, a)
Data.Tuple.swap (a, b) = (b, a)
您可以將任何內容傳遞給這些函式,它們將始終有效,因為它們的定義對這些物件可以做什么沒有任何假設,因為它們只是將它們打亂/丟棄。
相似地,
i' :: a
i' = 1
無法編譯,因為1不能表示任何型別的值a。例如,它不能表示 a String,而簽名i' :: a表示您可以放在i'任何地方的想法,例如Int預期 a 的位置,預期泛型Num的位置或預期的 aString等等。
換句話說,上面的簽名表明您可以i'在這兩個陳述句中使用:
j = i' 1
k = i' "str"
所以問題是:就像我們發現一些函式的簽名沒有以任何方式約束它們的引數一樣,我們是否有一個值可以包含在你能想到的每一種型別中?
是的,有一些類似的值,這里有兩個:
i' :: a
i' = error ""
j' :: a
j' = undefined
它們都是“底部”或⊥。
(1)“可用”是指當您在代碼編譯的某個地方撰寫它時,代碼會繼續編譯。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/528535.html
標籤:哈斯克尔约束
上一篇:Haskell函式導致堆疊溢位
