簡體   English   中英

為什么 GHC 在使用 Coercible 約束時會自相矛盾?

[英]Why is GHC contradicting itself when using a Coercible constraint?

為什么 GHC 從關聯數據的強制性推斷統一,為什么它與自己的檢查類型簽名相矛盾?

問題

{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}

module Lib
    ( 
    ) where

import Data.Coerce

class Foo a where
  data Bar a

data Baz a = Baz
  { foo :: a
  , bar :: Bar a
  }

type BarSame a b = (Coercible (Bar a) (Bar b), Coercible (Bar b) (Bar a))

withBaz :: forall a b. BarSame a b => (a -> b) -> Baz a -> Baz b
withBaz f Baz{..} = Baz
  { foo = f foo
  , bar = coerce bar
  }

這一切都很好——GHC 將愉快地編譯這段代碼,並且確信withBaz具有聲明的簽名。

現在,讓我們嘗試使用它!

instance (Foo a) => Foo (Maybe a) where
  data Bar (Maybe a) = MabyeBar (Bar a)

toMaybeBaz :: Baz a -> Baz (Maybe a)
toMaybeBaz = withBaz Just

給出了一個錯誤 - 但一個非常奇怪的錯誤:

withBaz Just
^^^^^^^^^^^^
cannot construct the infinite type: a ~ Maybe a

確實,如果我將 go 放入 GHCi,並要求它給我withBaz的類型:

ghc>:t withBaz
withBaz :: (b -> b) -> Baz b -> Baz b

那不是我給它的簽名。

強制力

我懷疑 GHC 正在處理 withBaz 的withBaz類型,就好像它們必須統一一樣,因為它是從Coercible (Bar a) (Bar b)推斷出Coercible ab 但是因為它是一個數據家族,所以它們甚至不需要是Coercible - 當然不是統一的。

更新!

以下更改修復了編譯:

instance (Foo a) => Foo (Maybe a) where
  newtype Bar (Maybe a) = MabyeBar (Bar a)

也就是說,將數據族聲明為新newtype ,而不是data 這似乎與 GHC 在語言中對Coercible的處理一致,因為

data Id a = Id a

不會導致生成Coercible實例 - 即使它絕對應該強制轉換a . 使用上述聲明,這將出錯:

wrapId :: a -> Id a
wrapId = coerce

但是使用newtype聲明:

newtype Id a = Id a

然后Coercible實例存在,並且wrapId編譯。

我相信@dfeuer 關於缺乏對類型/數據系列的“角色”支持的評論提供了答案。

對於頂級的、 data定義的參數化類型:

data Foo a = ...

Foo aFoo b類型的強制力取決於參數a的作用。 特別是,如果a的角色是名義上的,那么Foo aFoo b是可強制的當且僅當ab是完全相同的類型。

所以,在程序中:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RoleAnnotations #-}

import Data.Coerce

type role Foo nominal
data Foo a = Foo

foo :: (Coercible (Foo a) (Foo b)) => a -> b
foo = undefined

由於Foo a中參數anominal作用, foo的類型實際上被簡化為b -> b

λ> :t foo
foo :: b -> b

如果角色注釋從nominal更改為representational ,則類型簡化為Coercible ab => a -> b ,並且如果角色更改為phantom (此特定Foo聲明的默認值,因為a不會出現在右側),類型被簡化為a -> b 這一切都符合預期,並且對應於每個角色的定義。

請注意,如果您將Foo的聲明替換為:

data Foo a = Foo a

那么phantom角色將不再被允許,但其他兩個角色下foo的推斷類型將與以前完全相同。

但是,如果您從data切換到newtype ,則會有一個重要的區別。 和:

newtype Foo a = Foo a

您會發現,即使使用type role Foo nominal Foonominal , foo的推斷類型也會是Coercible ba => a -> b而不是b -> b 這是因為類型安全強制的算法處理newtype的方式與“等效” data類型不同,正如您在問題中所指出的那樣——只要構造函數位於 scope 中,無論類型參數。

因此,盡管如此,您對關聯數據系列的體驗與將系列的類型參數設置為的角色nominal 雖然我在 GHC 手冊中找不到它的記錄,但這似乎是設計的行為,並且有一張公開的票,承認所有數據/類型系列的所有參數都分配了nominal角色並建議放寬這個限制,@dfeuer 實際上從去年 10 月開始有一個詳細的建議

暫無
暫無

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

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