簡體   English   中英

為什么這個eta擴展是必要的?

[英]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)

在此類型簽名中明確指定了較高等級的ff'類型。 這使得類型推斷開始已經知道ff'的類型,因此能夠將它們與類型統一起來.

但是,如果你擺脫ff' ,那就是那種類型. 必須采取不知道。 不幸的是,系統無法推斷出更高級別的類型,因此您會遇到類型錯誤。

本質上,編譯器不能創建更高級別的類型來填充類型推斷期間的未知數,並且必須依賴於程序員注釋,我們需要名稱( ff'類型簽名來獲取這些注釋。

一個更容易理解的例子是更高級別的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 (.)

(上面我假裝abc是類型常量,以避免進一步的類型級lambda。)

重要的部分:

  • ff'用於特定類型。 也就是說,它們在被送到(.)之前被應用於類型級參數。
  • (.)在類型級別應用於類型( ff c等),它們不是ff'

正如您所看到的,原始代碼遠非微不足道。 類型推斷能夠添加所需的類型級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.

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