枚举类型类中的关联类型
Enumerating an associated type in a 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]
我不确定为什么它不能推断出 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#
type。这就像第一个解决方案一样工作,但使用 Proxy# a
类型而不是 proxy a
类型。当调用这样的函数时,你传递了 proxy#
,这是一个完全虚假的值。
给定如下所示的类型类定义,我想为作为 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]
我不确定为什么它不能推断出 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#
type。这就像第一个解决方案一样工作,但使用 Proxy# a
类型而不是 proxy a
类型。当调用这样的函数时,你传递了 proxy#
,这是一个完全虚假的值。