[英]Why is this eta expansion necessary?
有人可以幫我理解這個/給我一些閱讀材料嗎? 以下工作正常:
type F a b = Functor f => f a -> f b
fComp :: F b c -> F a b -> F a c
fComp f f' = f . f'
但是如果我改寫fComp = (.)
,那么類型檢查器會抱怨:
Couldn't match type ‘b0 -> f c’
with ‘forall (f1 :: * -> *). Functor f1 => f1 b -> f1 c’
(這個具體的例子並不是特別有用;我只是想縮小研究鏡頭時出現的例子。)
fComp
具有更高級別的類型和更高級別類型的類型推斷是非常有限的。 如果我們擴展類型同義詞,它可能會更容易理解(但更長!)
fComp :: forall f a b c. Functor f =>
(forall f1. Functor f1 => f1 b -> f1 c) ->
(forall f2. Functor f2 => f2 a -> f2 b) ->
(f a -> f c)
在此類型簽名中明確指定了較高等級的f
和f'
類型。 這使得類型推斷開始已經知道f
和f'
的類型,因此能夠將它們與類型統一起來.
。
但是,如果你擺脫f
和f'
,那就是那種類型.
必須采取不知道。 不幸的是,系統無法推斷出更高級別的類型,因此您會遇到類型錯誤。
本質上,編譯器不能創建更高級別的類型來填充類型推斷期間的未知數,並且必須依賴於程序員注釋,我們需要名稱( f
和f'
) 和類型簽名來獲取這些注釋。
一個更容易理解的例子是更高級別的id
函數:
myId :: (forall a. a) -> (forall b. b)
定義myId x = id x
編譯,但myId = id
給出以下錯誤:
/home/tikhon/Documents/so/eta-expansion-needed.hs:11:8:
Couldn't match type ‘b’ with ‘forall a. a’
‘b’ is a rigid type variable bound by
the type signature for myId :: (forall a. a) -> b
at /home/tikhon/Documents/so/eta-expansion-needed.hs:11:1
Expected type: (forall a. a) -> b
Actual type: b -> b
In the expression: id
In an equation for ‘myId’: myId = id
Failed, modules loaded: none.
(請記住, forall b. (forall a. a) -> b
與(forall a. a) -> (forall b. b)
。)
讓我使用類似System-F的表示法重寫示例,其中我們也傳遞類型。 下面, \\\\
代表類型抽象(大型lambda),以及字典抽象。 此外, @
代表類型/字典應用程序。
在此之前,請回憶一下(.)
的類型:
(.) :: forall a b c . (b -> a) -> (c -> b) -> (c -> a)
這是帶注釋的代碼(小心,不是為了膽小的人):
fComp :: F b c -> F a b -> F a c
fComp (f :: forall f1. Functor f1 => f1 b -> f1 c)
(f':: forall f2. Functor f2 => f2 a -> f2 b)
= \\ ff :: (* -> *) ->
\\ ffD :: Functor ff ->
((.) @ (ff c) @ (ff b) @ (ff a)) -- instantiated composition
(f @ ff @ ffD) -- first argument of (.)
(f' @ ff @ ffD) -- second argument of (.)
(上面我假裝a
, b
, c
是類型常量,以避免進一步的類型級lambda。)
重要的部分:
f
和f'
用於特定類型。 也就是說,它們在被送到(.)
之前被應用於類型級參數。 (.)
在類型級別應用於類型( ff c
等),它們不是f
和f'
正如您所看到的,原始代碼遠非微不足道。 類型推斷能夠添加所需的類型級lambda和應用程序。 在添加這些之后,我們不能簡單地再簽訂fComp
合同。
在pointfree變體中,類型推斷需要做的不僅僅是在有意義的情況下。 雖然fComp
的第一個參數是F ab
類型,但是(.)
的第一個參數必須是x -> y
的形式,它不會統一為F ab = forall g . ...
F ab = forall g . ...
實際上,沒有辦法成功解決下面的打字嘗試:
fComp :: F b c -> F a b -> F a c
fComp
= \\ ff :: (* -> *) ->
\\ ffD :: Functor ff ->
((.) @ ???a @ ???b @ ???c)
上面沒有???a
,...可以導致想要的類型。
唯一的可能性是實例化隱藏在F xy
類型中的forall
-quantified變量,但要做到這一點 ,我們需要一個點 。 編譯器可能會為您擴展該代碼,以便出現點,從理論上講可以實例化,但實際上不會。
(此外,eta擴展在Haskell中並不總是有效的:例如seq (undefined::()->()) 3
循環而seq (\\x->(undefined::()->()) x) 3
返回3
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.