[英]Enumerating an associated type in a typeclass
Given a typeclass definition like the following, I'd like to enumerate the MyClassId a
type for any type that is an instance of MyClass
. 给定一个类型类定义,就像下面,我想枚举
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
However, when I try to compile this snippet of code, GHC 7.10.2 complains with the following message: 但是,当我尝试编译这段代码时,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]
I am not exactly sure why it can't infer that the a
type variable is the same as the a
in the constraint for the function enumMyClassId
. 我不确定为什么它不能推断
a
类型变量与函数enumMyClassId
的约束中的a
相同。 One possible fix is to change the function enumMyClassId
to the following: 一种可能的解决方法是将函数
enumMyClassId
更改为以下内容:
enumMyClassId :: MyClass a => a -> [MyClassId a]
enumMyClassId _ = enumFrom minBound
But this is not very elegant as it introduces an unused variable just to make the program typecheck. 但这并不是很优雅,因为它引入了一个未使用的变量,只是为了使程序检查。 Is there some solution that doesn't involve the trick above?
是否有一些解决方案不涉及上述技巧?
The reason your function is rejected is that any attempt to use it will result in ambiguity. 您的函数被拒绝的原因是任何使用它的尝试都会导致歧义。 At the use site, you can give a signature that constrains
MyClassId a
, but you can't specify a
. 在使用网站,你可以给限制的签名
MyClassId a
,但你不能指定a
。 You should be able to defer the error message to the use site (where it will be easier to understand) by temporarily enabling AllowAmbiguousTypes
. 您应该能够通过临时启用
AllowAmbiguousTypes
将错误消息推迟到使用站点(更容易理解)。
There are two idiomatic fixes: 有两个惯用的修复:
enumMyClassId :: MyClass a => proxy a -> [MyClassId a]
You'd typically pass this a Proxy
from Data.Proxy
, but you can also use an arbitrary value whose type's last argument is a
. 您通常会从
Data.Proxy
传递此Proxy
,但您也可以使用其类型的最后一个参数为a
的任意值。
This approach is typically less convenient, but sometimes is important for getting things to be memoized. 这种方法通常不太方便,但有时对于记忆事物很重要。
Edward Kmett's tagged
package gives you Edward Kmett的
tagged
包给你
newtype Tagged s b = Tagged {unTagged :: b}
Then you'd write 然后你就写了
enumMyClassId :: MyClass a => Tagged a [MyClassId a]
And call it 并称之为
enumMyClassId :: Tagged a [MyClassId a]
There's also a GHC-special approach that's not even intended ever to be portable, but that gives performance like tagged types with proxy convenience. 还有一种GHC特殊的方法,甚至不打算随身携带,但它提供了类似标记类型的性能,并具有代理方便性。
You can use the magical Proxy#
type that GHC completely erases in compilation. 您可以使用GHC在编译时完全擦除的神奇的
Proxy#
类型 。 This works just like the first solution, but uses a Proxy# a
type instead of a proxy a
one. 这就像第一个解决方案一样,但是使用
Proxy# a
类型代替proxy a
。 When calling such a function, you pass proxy#
, which is a totally fake value. 调用这样的函数时,你传递
proxy#
,这是一个完全假的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.