Haskell 即席多态性

标签 haskell polymorphism overloading typeclass

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

但是,虽然下面的测试代码编译

{-# 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 )

最佳答案

您面临的问题是重载由类中的所有类型决定——包括仅作为返回类型出现的类型。您可以拥有两个 MyClass Bool Int 的实例和 MyClass Bool String ,并且它将能够根据预期的类型来消除歧义。

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

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

一旦为整个表达式的类型添加注释,它就不再模棱两可了,因为 ab是固定的。

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

class MyClass a b | a -> b where

当然,这确实会有意放弃一些灵 active :现在您不能同时拥有两个 MyClass Bool Int 的实例。和 MyClass Bool String .

关于Haskell 即席多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30039135/

相关文章:

haskell - 为什么这段代码会因 Int -> Int 而溢出?

代表有效类型的 Haskell 数据类型

generics - 通过特征而不是结构参数化变量?

c++ - 错误 - 变量类型 "X"是一个抽象类 - C++

C# 使用分层参数重载解析

haskell - 可折叠和幺半群类型

haskell - 如何使用准引用程序向 Database.Persist 模型添加唯一键?

c++ - 仅使用 getName 在主函数中设置字符串名称

overloading - 如何在不创建递归函数的情况下重载 Ada 中的 '=' 运算符?

c# - 重载匿名函数