简体   繁体   English

枚举类型类中的关联类型

[英]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: 有两个惯用的修复:

Use a proxy 使用代理

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的任意值。

Use a tagged type 使用标记类型

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特殊的方法,甚至不打算随身携带,但它提供了类似标记类型的性能,并具有代理方便性。

Magic proxy 魔术代理

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM