簡體   English   中英

為什么這個Haskell代碼使用fundeps進行類型檢查,但是對類型系列產生了不可觸摸的錯誤?

[英]Why does this Haskell code typecheck with fundeps but produce an untouchable error with type families?

給定一些類型定義:

data A
data B (f :: * -> *)
data X (k :: *)

...和這個類型類:

class C k a | k -> a

...這些(為了最小的例子的目的而高度設計)函數定義類型檢查:

f :: forall f. (forall k. (C k (B f)) => f k) -> A
f _ = undefined

g :: (forall k. (C k (B X)) => X k) -> A
g = f

但是,如果我們使用類型族而不是具有函數依賴性的類:

type family F (k :: *)

...然后等效的函數定義無法進行類型檢查:

f :: forall f. (forall k. (F k ~ B f) => f k) -> A
f _ = undefined

g :: (forall k. (F k ~ B X) => X k) -> A
g = f

• Couldn't match type ‘f0’ with ‘X’
    ‘f0’ is untouchable
      inside the constraints: F k ~ B f0
      bound by a type expected by the context:
                 F k ~ B f0 => f0 k
  Expected type: f0 k
    Actual type: X k
• In the expression: f
  In an equation for ‘g’: g = f

我閱讀了OutsideIn(X)論文的第5.2節,它描述了可觸摸和不可觸摸的類型變量,我有點理解這里發生了什么。 如果我添加一個額外的參數f是推動的選擇f內外部的forall ,那么程序typechecks:

f :: forall f a. f a -> (forall k. (F k ~ B f) => f k) -> A
f _ _ = undefined

g :: forall a. X a -> (forall k. (F k ~ B X) => X k) -> A
g = f

但是,在這個特定的例子中讓我特別困惑的是為什么函數依賴具有不同的行為。 我聽過人們在不同時間聲稱像這樣的功能依賴等同於類型族和等式,但這表明實際上並非如此。

在這種情況下,函數依賴提供了哪些信息,允許f以類型族沒有的方式實例化?

我不知道我是否應該將此作為答案發布,因為它仍然非常浪漫,但我確實認為這是本質上正在發生的事情:

要評估(C k (BX)) => X k值,您必須為k選擇具體類型,並指向滿足約束的instance C k (BX) 要做到這一點,你必須說出類型類' a參數的形式為B f ,編譯器可以從中提取f類型(並在這種情況下發現它是X ) - 重要的是,它可以在實際查找之前執行此操作在實例中 ,這將是f將變得不可觸及的點。

為了評估(F k ~ BX) => X k ,它有點不同。 在這里你不需要指向一個具體的實例,你只需要保證如果編譯器查找 F k類型家族,那么這個類型將與BX類型相同。 但是在實際查找實例之前,編譯器不能在此推斷F k具有形式B f ,因此也不會使用它來將f與外部量化參數統一起因為不可觸及。

因此,GHC的行為至少不是完全不合理的。 我仍然不確定它是否應該這樣做。

好的,我有機會玩這個。 有幾個分心:

在Type Family版本中,只有f的定義給出錯誤'f0' is untouchable (您可以使用AllowAmbiguousTypes來抑制它;這只是將錯誤推遲到g出現。)讓我們忽略g

然后,如果沒有AllowAmbiguousTypes ,f的錯誤消息會提供更多信息:

• Couldn't match type ‘f0’ with ‘f’
    ‘f0’ is untouchable
      inside the constraints: F k ~ B f0
      bound by the type signature for:
                 f :: F k ~ B f0 => f0 k
  ‘f’ is a rigid type variable bound by
    the type signature for:
      f :: forall (f :: * -> *). (forall k. F k ~ B f => f k) -> A
  Expected type: f0 k
    Actual type: f k

啊哈! rigid type variable問題。 我猜因為f是由k的等式約束修正的,這也是剛性的,因為......

轉到沒有gFunDep版本,我們可以在什么類型調用f 嘗試

f (undefined undefined :: X a)           -- OK
f (undefined "string"  :: X String)      -- rejected
f  Nothing                               -- OK
f (Just 'c')                             -- rejected

拒絕消息(對於X String示例)是

• Couldn't match type ‘k’ with ‘String’
  ‘k’ is a rigid type variable bound by
    a type expected by the context:
      forall k. C k (B X) => X k
  Expected type: X k
    Actual type: X String
• In the first argument of ‘f’, namely
    ‘(undefined "string" :: X String)’

注意消息是關於k而不是 f ,它是從FunDep確定的 - 或者是我們能找到合適的k

說明

函數f的簽名表示k是存在量化/更高等級。 然后我們不能允許關於k任何類型信息逃逸到周圍的上下文中。 我們不能為k提供任何(非底部)值,因為它的類型會侵入forall

這是一個更簡單的例子:

f2 :: forall f. (forall k. f k) -> A
f2 _ = undefined

f2 Nothing                                 -- OK
f2 (Just 'c')                              -- rejected rigid type var

因此原始的FunDep版本編譯是一個分心:它不能有人居住。 (根據我之前的懷疑,這是FunDep的常見症狀。)

暫無
暫無

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

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