簡體   English   中英

Haskell中的類型類行為

[英]Typeclass behavior in Haskell

作為Haskell的初學者,我很難理解為什么會失敗

-- this works fine of course
f :: Float
f = 1.0

f :: Num a => a
f = 1.0
-- Could not deduce (Fractional a) arising from the literal ‘1.0’
--      from the context: Num a
--        bound by the type signature for:
--                   f :: forall a. Num a => a

我的困惑源於他們都有Num的例子。 因此,由於Int,Integers,Doubles等都有Num類型類的實例,為什么我不能在f填充任何數值?

例如, negate它有一個簽名negate :: Num a => a -> a將與廉政局的浮動雙打等工作

任何見解將不勝感激。

這里的問題是,當你寫f :: Num a => a ,這意味着f必須為所有可能的實例運行a使得Num a實例存在。 特別是,這意味着在其他地方寫入(f :: Int)應該可以正常工作,因為instance Num Int肯定存在。 但是,為f返回的值是1.0 ,它不是整數: 1.0 :: Fractional p => p 錯誤消息基本上是說“知道a是一個Num並沒有告訴我們a是一個Fractional ,所以分數字面1.0沒有辦法讓類型為a ”。

想想它的一種方法是, 調用者就可以選擇什么a應該是:這就是為什么這種形式的類型簽名被稱為普遍量化

您可能正在考慮存在量化: f返回某種Num ,但“調用者”不知道Num返回了什么。 這通常不如通用量化那么有用,並且在Haskell中有點笨拙,但可以像這樣完成:

{-# LANGUAGE ExistentialQuantification #-} -- This is a GHC extension
-- A SomeNum value contains some kind of Num. Can't see what kind from the "outside".
data SomeNum = forall a. Num a => SomeNum a
f :: SomeNum
f = SomeNum 5.0

在Haskell中,像f :: a這樣的簽名是(概念上更清晰的) f :: forall a. a語法f :: forall a. a f :: forall a. a ,其中forall很像“類型級lambda”(而不是f :: exists a. a你可能一直在考慮)。 事實上,如果你看一下GHC Core,你會看到所有這些類型的應用程序都是顯式的:每當你使用一個通用量化的函數時,“調用者”顯式地傳入要用於每個類型變量的類型。

但是,我建議您不要在此階段嘗試使用存在量化:通常有更好/更容易的替代方案。 我只是想解釋它,以幫助顯示存在與普遍性之間的區別。

為什么我不能在f填充任何數值?

例如,具有簽名negate :: Num a => a -> a將適用於IntFloatDouble s等。

正因為如此!

你不能在f定義中填充任何數值,因為你不能在negate定義中填充任何數值。

假設我們試圖定義negate如下

negate :: Num a => a -> a
negate x = 10.5 - 10.5 - x

這會對Int工作嗎? 也就是說, negate 42 :: Int 不,因為10.5不是Int

由於negate的類型承諾它適用於任何數字類型,包括Int ,但它實際上不適用於Int ,因此承諾被破壞。 靜態類型檢查因此拒絕該代碼。

同樣,如果接受了類型檢查

f :: Num a => a
f = 10.5

然后所有這些都應該工作: f + 8 :: Intf / 2 :: Doublef - 4 :: Integer 但是10.5不適合Int (也不適合Integer )。

這里的問題是f :: Num a => a允許調用者選擇任何數字類型a 由於類型允許調用者選擇, f選擇,但必須適應調用者做出的任何選擇。 因此, f不能使用僅適用於某些數字類型的代碼,而不能使用其他類型的代碼。

如果f僅適用於Fractional類型( Num類型的子集),則f的類型必須向調用者通告其選擇僅限於小數類型。 這是通過使用f :: Fractional a => a來完成的。

如果你只是寫

f = 10.5

你回來了f :: Fractional a => a沒有問題。

當你明確聲明它的類型為Num a => a ,Haskell必須統一聲明的和實際的類型,而它不能,因為FractionalNum子類

>> :i Fractional
class Num a => Fractional a where
  --  ^^^                              -- Fractional is Num's subclass
  ...........
  fromRational :: Rational -> a
  ...........

每個Fractional都是一個Num ,但不是每個Num都是Fractional


fromRational的類型來自fromRational :: Fractional a => Rational -> a

這表明像10.5這樣的浮點文字實際上是從fromRational (10.5 :: Rational)讀取的,就像整個數字文字(如10被讀作fromInteger (10 :: Integer)

實際上, Haskell教程中的第10.3節內容如下:

整數(不帶小數點)實際上等同於fromInteger到數字值作為Integer 類似地,浮動數字(帶小數點)被視為fromRational到數字值作為Rational 因此, 7具有類型(Num a) => a ,並且7.3具有類型(Fractional a) => a

當你給編譯器類型聲明一樣f :: Num a => a ,你不只是說f有類型類Num但是這一切 ,你知道它在這里。 由於您輸入的是1.0而不是1 ,因此編譯器斷定您還需要Fractional 你的聲明說你沒有,所以這不能編譯。 如果你說它是Fractional a => a ,它會編譯,而Fractional表示Num

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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