给定类似于以下的类型类定义,我想枚举 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
但是,当我尝试编译这段代码时,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
但这并不是很优雅,因为它引入了一个未使用的变量,只是为了使程序检查 . 是否有一些解决方案不涉及上述技巧?
1 回答
您的函数被拒绝的原因是任何使用它的尝试都会导致歧义 . 在使用站点,您可以提供约束
MyClassId a
的签名,但不能指定a
. 您应该能够通过临时启用AllowAmbiguousTypes
将错误消息推迟到使用站点(更容易理解) .有两个惯用的修复:
使用代理
您通常会从
Data.Proxy
传递Proxy
,但您也可以使用其类型的最后一个参数为a
的任意值 .使用标记类型
这种方法通常不太方便,但有时对于记忆事物很重要 .
Edward Kmett的
tagged
包给你然后你就写了
并称之为
还有一种GHC特殊的方法,甚至不打算随身携带,但它提供了类似标记类型的性能,并具有代理方便性 .
魔术代理
您可以使用GHC在编译时完全擦除的神奇Proxy# type . 这与第一个解决方案类似,但使用
Proxy# a
类型而不是proxy a
类型 . 调用这样的函数时,传递proxy#
,这是一个完全假的值 .