给定类似于以下的类型类定义,我想枚举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
类型变量与函数a
约束中的enumMyClassId
相同。一种可能的解决方法是将函数enumMyClassId
更改为以下内容:
enumMyClassId :: MyClass a => a -> [MyClassId a]
enumMyClassId _ = enumFrom minBound
但这不是很优雅,因为它引入了一个未使用的变量,只是为了使程序成为类型检查。是否有一些解决方案不涉及上述技巧?
答案 0 :(得分:3)
您的功能被拒绝的原因是任何使用的尝试都会导致歧义。在使用网站上,您可以提供约束MyClassId a
的签名,但不能指定a
。您应该能够通过暂时启用AllowAmbiguousTypes
将错误消息推迟到使用网站(更容易理解)。
有两个惯用修复:
enumMyClassId :: MyClass a => proxy a -> [MyClassId a]
您通常会从Proxy
传递Data.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#
,这是一个完全假的值。