簡體   English   中英

枚舉類型類中的關聯類型

[英]Enumerating an associated type in a typeclass

給定一個類型類定義,就像下面,我想枚舉MyClassId a類型的任何類型的實例MyClass

{-# LANGUAGE TypeFamilies     #-}
{-# LANGUAGE FlexibleContexts #-}

class
  ( Enum (MyClassId e)
  , Bounded (MyClassId e))
  => MyClass e where
    type MyClassId e :: *

enumMyClassId :: MyClass a => [MyClassId a]
enumMyClassId = enumFrom minBound

但是,當我嘗試編譯這段代碼時,GHC 7.10.2會抱怨以下消息:

enumTypeClass.hs:12:18:
    Couldn't match type ‘MyClassId a0’ with ‘MyClassId a’
    NB: ‘MyClassId’ is a type function, and may not be injective
    The type variable ‘a0’ is ambiguous
    Expected type: [MyClassId a]
      Actual type: [MyClassId a0]
    In the ambiguity check for the type signature for ‘enumMyClassId’:
      enumMyClassId :: forall a. MyClass a => [MyClassId a]
    To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
    In the type signature for ‘enumMyClassId’:
      enumMyClassId :: MyClass a => [MyClassId a]

我不確定為什么它不能推斷a類型變量與函數enumMyClassId的約束中的a相同。 一種可能的解決方法是將函數enumMyClassId更改為以下內容:

enumMyClassId :: MyClass a => a -> [MyClassId a]
enumMyClassId _ = enumFrom minBound

但這並不是很優雅,因為它引入了一個未使用的變量,只是為了使程序檢查。 是否有一些解決方案不涉及上述技巧?

您的函數被拒絕的原因是任何使用它的嘗試都會導致歧義。 在使用網站,你可以給限制的簽名MyClassId a ,但你不能指定a 您應該能夠通過臨時啟用AllowAmbiguousTypes將錯誤消息推遲到使用站點(更容易理解)。

有兩個慣用的修復:

使用代理

enumMyClassId :: MyClass a => proxy a -> [MyClassId a]

您通常會從Data.Proxy傳遞此Proxy ,但您也可以使用其類型的最后一個參數為a的任意值。

使用標記類型

這種方法通常不太方便,但有時對於記憶事物很重要。

Edward Kmett的tagged包給你

newtype Tagged s b = Tagged {unTagged :: b}

然后你就寫了

enumMyClassId :: MyClass a => Tagged a [MyClassId a]

並稱之為

enumMyClassId :: Tagged a [MyClassId a]

還有一種GHC特殊的方法,甚至不打算隨身攜帶,但它提供了類似標記類型的性能,並具有代理方便性。

魔術代理

您可以使用GHC在編譯時完全擦除的神奇的Proxy#類型 這就像第一個解決方案一樣,但是使用Proxy# a類型代替proxy a 調用這樣的函數時,你傳遞proxy# ,這是一個完全假的值。

暫無
暫無

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

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