当前位置: 首页 > 面试题库 >

参数多态性与Ad-hoc多态性

姚嘉容
2023-03-14
问题内容

我想了解参数多态性(例如Java / Scala / C
++语言中的通用类/函数的多态性)与Haskell类型系统中的“即席”多态性之间的主要区别。我熟悉第一种语言,但是我从未与Haskell合作。

更确切地说:

  1. 例如Java中的类型推断算法与Haskell中的类型推断有何不同?
  2. 请给我举一个例子,这种情况可以用Java / Scala编写但不能用Haskell编写(根据这些平台的模块化功能),反之亦然。

提前致谢。


问题答案:

由于每TAPL,§23.2:

参数多态性(…),允许使用变量代替实际类型来“通用”键入单个代码,然后根据需要使用特定类型进行实例化。参数定义是统一的:它们的所有实例的行为都相同。(…)

相比之下,即席多态性使“多态性”值在不同类型下“显示”时表现出不同的行为。即席多态性的最常见示例是重载,该重载将单个功能符号与许多实现相关联。编译器(或运行时系统,取决于重载分辨率是静态的还是动态的)根据参数的类型为函数的每个应用程序选择适当的实现。

因此,如果考虑历史的连续阶段,非通用官方Java(又名J2SE
5.0之前的版本
,2004年9月之前)具有即席多态性-
因此您可以重载方法
-但不能实现参数多态性,因此您不能吨写一个通用的方法。之后,您当然可以两者都做。

相比之下,自1990年成立以来,Haskell就具有参量多态性,这意味着您可以编写:

swap :: (A; B) -> (B; A)
swap (x; y) = (y; x)

其中A和B是类型变量,无需假设即可实例化为 所有 类型。

但是,没有预先存在的结构可以提供 即席 多态性,该结构旨在让您编写适用于 几种 (但 不是全部) 类型的函数。实现类型类是实现此目标的一种方式。

它们使您可以描述一个 (类似于Java接口),并提供要为通用类型实现的函数的 类型签名 。然后,您可以注册一些(并且希望有 几个
)与该类匹配的 实例 。同时,您可以编写一个通用方法,例如:

between :: (Ord a)  a -> a -> a -> Bool
between x y z = x ≤ y ^ y ≤ z

其中Ord是定义函数的类(_ ≤ _)。当使用时,将(between "abc" "d" "ghi")静态解析
以为字符串(而不是整数)选择正确的 实例 -恰好在(Java)方法重载发生的那一刻。

您可以使用有界通配符在Java中执行类似的操作。但是
在这方面Haskell和Java之间主要区别在于,只有Haskell可以自动进行字典传递 :在两种语言中,给定Ord T,,b0和的两个实例b1,您可以构建一个函数f,将它们作为参数并为该对生成实例。输入(b0, b1),例如,按字典顺序使用。现在说你被给予了(("hello", 2), ((3, "hi"), 5))。在Java中,你必须记住的实例为stringint,并通过正确的实例(由四个应用程序f才能申请!)between到该对象。Haskell可以运用组合性,并找出如何仅在基础实例和f构造函数的情况下构建正确的实例(当然,这会扩展到其他构造函数)。

现在,就 类型推断 而言(这可能是一个不同的问题),对于两种语言而言,它都是 不完整的 ,从某种意义上说,您始终可以编写一个 未注释的
程序,而编译器将无法为其确定程序。方式。

  1. 对于Haskell来说,这是因为它具有强制性(也称为一流)多态性,对于该类型性,无法推断类型。请注意,在这一点上,Java仅限于一阶多态性(Scala扩展的东西)。

  2. 对于Java,这是因为它支持反向子类型化。

但是这些语言的主要区别在于在实践中 类型推断适用的程序语句范围以及 类型推断结果 正确性重要性

  1. 对于Haskell而言,推论适用于所有“非高度多态”术语,并基于已发布的著名算法扩展,努力返回声音结果:

    • Haskell的推理本质上基于Hindley-Milner,当您推断应用程序的类型时, 类型变量 (例如上例中的AB)只能用 非多态 类型实例化(我正在简化,但这实际上是您可以在例如Ocaml中找到的ML风格的多态性。
    • 最近的GHC将确保仅对于具有非Damas-Milner类型的let绑定或λ抽象才需要类型注释。
    • Haskell甚至在其最繁琐的扩展(例如GADT)中都尝试相对保持与这个不可理解的核心的相对距离。无论如何,提议的扩展几乎总是出现在带有扩展类型推断 正确性 证明的论文中。
    • 对于Java,无论如何,类型推断都以 更为有限的方式 适用:

在Java 5发行之前,Java中没有类型推断。根据Java语言文化, 程序员必须显式声明每个变量,方法和动态分配的对象的类型 。在Java
5中引入泛型(按类型参数化的类和方法)后, 该语言保留了对变量,方法和分配的这一要求
。但是多态方法(按类型参数化)的引入要求(i)程序员在每个多态方法调用站点提供方法类型参数,或者(ii)语言支持方法类型参数的推断。为了避免给程序员带来额外的文书负担,Java
5的设计人员选择执行类型推断来确定类型参数 用于多态方法调用
。(来源,重点是我的)

该推理算法基本上是GJ的,但有几分
缺憾此外通配符作为一种事后(请注意,我不是最新的在J2SE
6.0所做的可能修正,虽然)。方法上的巨大概念差异是Java的推断是 局部的
,从某种意义上说,表达式的推断类型仅取决于从类型系统生成的约束及其子表达式的类型,而不取决于上下文。

请注意,关于类型不完整(有时甚至是错误的类型推断)的聚会线相对宽松。根据规格:

还要注意,类型推断不会以任何方式影响健全性。如果推断的类型是荒谬的,则调用将产生类型错误。类型推断算法应被视为一种启发式算法,旨在在实践中很好地进行表现。如果无法推断出预期的结果,则可以使用显式类型参数。



 类似资料:
  • 是否可以在 Swagger 中定义多种类型的输入参数? 示例:我有一个使用URL < code > http://localhost/tasks/{ taskId } 处理资源的API。但是每个任务都包含整数id和字符串uuid。我希望允许用户通过id或uuid来寻址资源,这样< code > http://localhost/tasks/123 和< code > http://localhos

  • https://wiki.haskell.org/polymorphism说 特别多态性是指当一个值能够采用几种类型中的任何一种时,因为它或它使用的值已经为这些类型中的每一种类型赋予了单独的定义。例如,+运算符在应用于浮点值时所做的事情与应用于整数时完全不同--在Python中,它甚至可以应用于字符串。大多数语言至少支持一些特殊的多态性,但在像C这样的语言中,它仅限于内置的函数和类型。其他语言,如

  • 我试图在c#中创建一个参数多态性函数: 我想让T成为类型变量。但是我得到了一个关于类的错误,没有找到T。 有人知道如何解决这个问题吗? 谢谢。

  • 我在比较C++多态性的以下方法的性能: 方法[1]。使用boost变体的静态多态性,每个方法都有一个单独的访问者method[2]。静态多态性使用boost变体,单个访问者调用不同的方法,使用方法重载方法[3]。平原老动态多态性 一些发现: 方法[1]似乎明显优于方法[2]和[3] 方法[3]在大多数情况下都优于方法[2] 我的问题是,为什么方法[2]在我使用一个访问者但使用方法重载来调用正确的方

  • 问题内容: 继承和多态都构成IS- A关系吗?在运行时发生继承和“重写”多态性而在编译时发生“过载”多态性是真的吗?我之所以这样问,是因为许多论坛似乎给出了相互矛盾且常常令人困惑的答案。 谢谢! 问题答案: 对于问题的第一部分,我认为维基百科提供了一个很好的定义: 在面向对象的程序设计中,子类型多态或包含多态是类型理论中的一个概念,其中名称可以表示许多不同类的实例,只要它们与某个公共超类相关即可。

  • 教学目标 了解多态性的概念 了解怎样声明和使用实现多态性的虚函数 了解抽象类和具体类的区别 学会怎样声明建立抽象类的纯虚函数 认识多态性是如何扩展和维护系统 了解 C++ 如何实现虚函数和动态关联