[英]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 a
和Foo b
類型的強制力取決於參數a
的作用。 特別是,如果a
的角色是名義上的,那么Foo a
和Foo b
是可強制的當且僅當a
和b
是完全相同的類型。
所以,在程序中:
{-# 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
中參數a
的nominal
作用, 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.