Haskell ad hoc多态性

时间:2015-05-04 20:13:17

标签: haskell polymorphism overloading typeclass

我试图了解haskell中的ad-hoc多态性,即具有相同的函数为不同的参数类型提供不同的行为。

但是,以下测试代码编译

{-# LANGUAGE MultiParamTypeClasses #-}

class MyClass a b where
    foo :: a -> b

instance MyClass Bool Int where
    foo True = 0
    foo False = 1

instance MyClass Double Double where
    foo x = -x

如果我尝试用

之类的东西来调用它
foo True

ghci对我大吼:

No instance for (MyClass Bool b0) arising from a use of `foo'
The type variable `b0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
  instance MyClass Bool Int -- Defined at test.hs:6:10
Possible fix: add an instance declaration for (MyClass Bool b0)
In the expression: foo True
In an equation for `it': it = foo True

但是,如果我指定了返回类型,它可以工作:

foo True :: Int -- gives 0

为什么需要这个? Bool的参数类型应足以解决歧义。

另外:这是最好的"实现类似行为的方式? (不将功能重命名为fooBoolfooDouble

2 个答案:

答案 0 :(得分:10)

您遇到的问题是,重载是由类中所有类型确定的 - 包括仅作为返回类型出现的类型。您可以拥有MyClass Bool IntMyClass Bool String的实例,并且可以根据预期的类型消除歧义。

与Haskell类型类的核心设计权衡之一是开放世界假设"。 Haskell类型实例是隐式全局的:特定类型(或类型序列,在本例中)在整个程序中只能有一个实例,它会隐式导出到使用该类型的所有模块。

这使得很容易在没有意识到的情况下获得某个类的新实例,因此Haskell类型检查器假定可能的实例可能存在于任何有效的类型组合中。在您的情况下,这意味着虽然MyClass Bool Int是使用Bool的唯一实例,但它与其他可能的MyClass Bool b实例仍然不明确。

为整个表达式的类型添加注释后,它会不明确,因为ab都是固定的。

要获得您期望的行为,您可以使用FunctionalDependencies。这些允许您为任何给定的b指定只有一个可能a,这将让GHC正确地推断出类型。它看起来像这样:

class MyClass a b | a -> b where

当然,这确实会产生一些灵活性:现在你不能拥有MyClass Bool IntMyClass Bool String的实例。

答案 1 :(得分:7)

Tikhon Jelvis 详细阐述了这个问题并建议使用功能依赖,但还有另一种选择:type families。您的代码变为

{-# LANGUAGE TypeFamilies #-}

class MyClass a where
    type R a
    foo :: a -> R a

instance MyClass Bool where
    type R Bool = Int
    foo True  = 0
    foo False = 1

instance MyClass Double where
    type R Double = Double
    foo x = -x

我们在这里使用associated type synonyms。我喜欢这些显式类型级别的函数,但是如果你不喜欢,可以使用fundeps,因为differences相当微妙。