Haskell 枚举typeclass中的关联类型

Haskell 枚举typeclass中的关联类型,haskell,typeclass,Haskell,Typeclass,给定如下所示的typeclass定义,我想为属于MyClass实例的任何类型枚举MyClassId a类型 {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} class ( Enum (MyClassId e) , Bounded (MyClassId e)) => MyClass e where type MyClassId e :: * enumMyClassId :: MyC

给定如下所示的typeclass定义,我想为属于
MyClass
实例的任何类型枚举
MyClassId a
类型

{-# 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]
但是这不是很优雅,因为它引入了一个未使用的变量,只是为了进行程序类型检查。有没有不涉及上述技巧的解决方案?

您的函数被拒绝的原因是任何使用它的尝试都会导致歧义。在使用站点,您可以提供约束
MyClassId a
的签名,但不能指定
a
。通过临时启用
AllowAmbiguousTypes
,您应该能够将错误消息延迟到使用站点(在那里更容易理解)

有两种惯用的修复方法:

使用代理 通常,您会从
Data.Proxy
中传递一个
代理
,但也可以使用类型的最后一个参数为
a
的任意值

使用标记的类型 这种方法通常不太方便,但有时对于将事情记录下来很重要

Edward Kmett的
标签
软件包为您提供

newtype Tagged s b = Tagged {unTagged :: b}
然后你会写信

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

enumMyClassId :: Tagged a [MyClassId a]

还有一种GHC特殊的方法,它甚至不打算是可移植的,但它提供了类似于标记类型的性能,并且具有代理便利性

魔法代理
您可以使用GHC在编译中完全擦除的魔法。这与第一个解决方案类似,但使用
代理#a
类型而不是
代理a
类型。调用这样一个函数时,传递的是
proxy 35;
,这是一个完全虚假的值。

您可能还喜欢包族,它提供了
universe::universe a=>[a]
作为枚举给定类型的规范方法。它有许多不在
(Enum a,Bounded a)
约束范围内的实例,并且跳过了一些可能会被约束范围内的实例,但方式是错误的,比如
Double
,将约束从类实例移动到
enumMyClassId
的类型签名,例如
enumMyClassId::(有界(MyClassId a),Enum(MyClassId a))=>[a]
并在
MyClass
声明中跳过该约束。@DanielWagner,当然,如果您出于其他原因不需要/想要该约束。
enumMyClassId :: MyClass a => Tagged a [MyClassId a]
enumMyClassId :: Tagged a [MyClassId a]