2016-08-27 67 views
20
Prelude> :i ($) 
($) :: 
    forall (r :: GHC.Types.RuntimeRep) a (b :: TYPE r). 
    (a -> b) -> a -> b 
     -- Defined in ‘GHC.Base’ 
infixr 0 $ 

(a -> b) -> a -> b有什麼不同?有沒有適合新型簽名的b

+3

舊簽名是多年來的謊言。新的簽名實際上是更一般的,包括提升類型(kind *')和未提升類型(以前的'#';現在分成更多類型)。 – dfeuer

+8

雖然不是一個完全重複的(我認爲這個問題可以從更新的答案中獲益),[這個答案](http://stackoverflow.com/a/35320729/465378)包含了一些可以幫助解釋事物的信息。 –

+0

@sevo什麼語言的擴展打開了這個?我從其他一些討論中知道這一點,但無法觸發它。我只是得到'($)::(a - > b) - > a - > b' – Michael

回答

4

8.0之前,在typechecker中有一個特殊情況,使得$的申請到未解除種類的工作。這也意味着你無法定義自己的功能,可以同時使用提升和未提升的類型。現在這個所謂的Levity Polymorphsim('輕量級'是指'某種東西被提升的程度'或'提升性',因爲'未提升'和'提升'類型)被嵌入到類型檢測器中,這是可能的:

import GHC.Exts (TYPE, RuntimeRep(..)) 
import Data.Kind (type (*)) 

ap :: forall (a :: *) (b :: *) . (a -> b) -> (a -> b) 
ap f x = f x 

ap_LP :: forall (a :: *) (b :: TYPE r) . (a -> b) -> (a -> b) 
ap_LP f x = f x 

乃至$功能現在同樣定義爲ap_LP,隨着typechecker不需要特殊的情況下,使$工作與功能恢復未提升的類型(後面還有一個在typechecker的特殊情況,使多態應用,即runST $ ...工作,但這與levity多態性無關)。這實際上是增加了複雜性的原因 - 現在類型系統中的「黑客」較少,GHC的用戶可以通過給函數適當的類型來利用輕量級多態性(請注意,從未推斷出輕量級多態類型,據我所知)。在輕量級多態性之前,如果您想編寫一個多態函數,可以在提取和未提取類型上工作,您有義務使用不同類型簽名編寫兩個相同的函數副本。

新類型從舊的不同點在於新的類型是比舊的嚴格更一般:

-- ok 
ap' :: forall (a :: *) (b :: *) . (a -> b) -> (a -> b) 
ap' = ap_LP 

-- type error: 
-- * Couldn't match a lifted type with an unlifted type 
ap_LP' :: forall (a :: *) (b :: TYPE r) . (a -> b) -> (a -> b) 
ap_LP' = ap 

換句話說,每b其中「適應」老標記必須(通過定義)符合新型簽名(所以這種改變完全向後兼容!)。


還要注意的是,以下是可能:

ap'' :: forall (a :: TYPE r) (b :: *) . (a -> b) -> (a -> b) 
ap'' f x = f x 

產生的誤差是

A representation-polymorphic type is not allowed here: 
    Type: a 
    Kind: TYPE r 
In the type of binder `x' 

和SPJ解釋了限制here的原因:

($)的第二個參數不能有一個 取消裝箱的類型是絕對正確的。因爲($)的代碼必須圍繞 (傳遞給函數)移動該參數,所以它必須知道它的寬度,指針等。

但是實際上,將電話(f $ x)的結果解壓爲 就可以了,因爲($)的代碼不會混淆結果;它 只是尾調用f。

這就是說,不是每一個輕率多態性類型具有有效的居民 - 這涉及裝箱和盒裝類型之間的操作的區別,這隻能在某些情況下均勻的處理,並且typechecker確保的。