Haskell 枚举typeclass中的关联类型
给定如下所示的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
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]