MultiParamTypeClasses的歧义问题

时间:2015-03-05 20:40:22

标签: haskell

我有一个像这样定义的类型类:

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是否解决了这个问题?

3 个答案:

答案 0 :(得分:3)

Condition v a要求解析va。由于v=>的右侧未提及analyze,因此编译器无法选择正确的v。它与foo :: Show a => ()foo :: Condition a x => x -> Bool相同。你如何选择a?我不知道对Condition的解释是什么,但可能是v应该由a确定的情况 - 如果是这种情况,那么你可以写

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,因此您可能已在范围内ProxyData.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

经历的报告