我有一个像这样定义的类型类:
class (Eq a, Show a, Typeable a) => Condition v a where
(以及一些方法)
我想编写一个函数,它接受两个条件都相同' v'。
analyze :: (Condition v a, Condition v b) => a -> b -> Bool
(为简单起见,返回类型使Bool)
但是当我尝试编译时,我得到了
Could not deduce (Condition v0 b)
arising from the ambiguity check for ‘analyze’
from the context (Condition v a, Condition v b)
因此,它不会推断出我想说的内容,而是引入了另一个类型变量v0。
你能解释一下我所写的类型签名的错误以及如何解决这个错误吗? RankNTypes是否解决了这个问题?
答案 0 :(得分:3)
Condition v a
要求解析v
和a
。由于v
中=>
的右侧未提及analyze
,因此编译器无法选择正确的v
。它与foo :: Show a => ()
或foo :: Condition a x => x -> Bool
相同。你如何选择a
?我不知道对Condition
的解释是什么,但可能是v
应该由a
确定的情况 - 如果是这种情况,那么你可以写} p>
class Condition v a | a -> v
,称为功能依赖。
答案 1 :(得分:1)
如另一个答案所述,功能依赖可以解决问题。我想指出另一种选择:使用类型代理。
class (Eq a, Show a, Typeable a) => Condition v a where
-- Introduce v into the type of analyze, without demanding that a
-- value of type v be provided; instead, just give some value which
-- indicates v as a type parameter (like a Proxy, for instance).
analyze :: (Condition v a, Condition v b) => proxy v -> a -> b -> Bool
instance Condition Bool Bool where
...
instance Condition Int Bool where
...
-- Use a proxy to pick out the type of v.
exampleInt = analyze (Proxy :: Proxy Int) True False
exampleBool = analyze (Proxy :: Proxy Bool) True False
由于您使用的是Typeable
,因此您可能已在范围内Proxy
,Data.Typeable
将其导出。
使用函数依赖关系排除上述示例:对于同一Condition
,您不能拥有两个a
实例。也许那很好,但如果没有,请考虑使用类型代理。
答案 2 :(得分:1)
另一种方法是使用type families而不是函数依赖(这也消除了对多参数类型类的需要):
class (Eq a, Show a, Typeable a) => Condition a where
type V a :: *
analyze :: (Condition a, Condition b, V a ~ V b) => a -> b -> Bool
回答OP的评论:类型族"也可能被视为功能依赖的替代品,但提供了比函数依赖关系样式更具功能性的类型级编程风格"。它们后来被添加到GHC中,因此许多较旧的库仍然使用功能依赖。 TF和FD之间存在一些差异(例如,处理重叠实例的方式),但我认为通常认为对于较新的代码,TF是更好的方法。 这是一份概述converting FD to TF
经历的报告