2015-02-11 69 views
10

我的意思是,例如,爲什麼f =(+)不需要類型註釋?

f :: (Enum a) => a -> a --without this line, there would be an error 
f = succ 

這是因爲succ需要它的參數是枚舉(succ :: (Enum a) => a -> a

(+)

f = (+) --ok 

雖然(+)的聲明(+) :: (Num a) => a –> a –> a

我的意思是,我爲什麼不需要聲明ff :: (Num a) => a –> a –> a

回答

14

由於違約。Num是一個'可違約'類型的類,這意味着如果你不加限制,編譯器會對你打算使用它的類型做出一些智能猜測。嘗試將該定義放入模塊中,然後運行

:t f 

in ghci;它應該告訴你(IIRC)f :: Integer -> Integer -> Integer。編譯器不知道你想用哪個a,所以它猜到了Integer;並且自那以後工作,它符合那個猜測。

爲什麼它不推斷f的多態類型?由於可怕的[1]單態限制。當編譯器看到

f = (+) 

它認爲「f是值」,這意味着它需要單個(單態)型。 ETA-擴大定義

f x = (+) x 

,你會得到的多態型

f :: Num a => a -> a -> a 

,同樣,如果你ETA-擴大你的第一個定義

f x = succ x 

你不需要再輸入一次簽名。

[1] GHC文檔的實際名稱!

+0

何時會發生這種減少? ghci中的'f =(+)'不受限制。 – Bergi 2015-02-11 22:59:43

+1

@Bergi - 答案在這裏:http://stackoverflow.com/questions/28336108/why-is-22-0-double-in-a-hs-file-but-fractional-aa-in-ghci/28336620 #28336620 – 2015-02-11 23:52:53

7

我的意思是,爲什麼我不需要聲明f(+) :: (Num a) => a –> a –> a

如果您完全聲明f的簽名,您確實需要這麼做。但是,如果你不這樣做,編譯器會「猜測」在這種情況下,簽名本身–這還不是全部,以顯着的,因爲它可以基本上只是複製粘貼&的(+)簽名。而這正是它會做的。

...或者至少它應該做什麼。它的確如此,只要你有-XNoMonomorphism標誌。否則,dreaded monomorphism restriction步驟因爲f的定義是形狀ConstantApplicativeForm = Value;這使得編譯器將簽名啞變爲下一個最好的非多態類型,它可以找到,即Integer -> Integer -> Integer。爲了防止這種情況發生,您應該事實上爲所有頂級功能提供正確的簽名。這也防止了很多混淆,並且許多錯誤變得不太混亂。

的單態的限制是原因

f = succ 

不會對自己的工作:因爲它也有這個CAF形狀,編譯器不會嘗試推斷正確的多態類型,但試圖找到一些具體實例化以形成單形簽名。但與Num不同,Enum類不提供默認實例。

可能的解決辦法,按優先順序排列:

  1. 始終添加簽名。你真的應該。
  2. 啓用-XNoMonomorphismRestriction
  3. f a = succ af a b = a+b的形式寫出你的函數定義。因爲有明確提到的參數,這些沒有資格作爲CAF,所以單態的限制不會踢。
0

哈斯克爾默認Num約束IntInteger,我忘了。