鸭子打字和通用编程

Duck typing and generic programming

本文关键字:编程 鸭子      更新时间:2023-10-16

我在SO中搜索了一段时间,没有找到一个明确而笼统的答案,只有一些矛盾和特殊的意见。[1]

所以我想知道鸭子类型和泛型编程之间的关系是什么?(DT<GP,DT==GP,DT>GP)。通过泛型编程,我特别提到C++模板或Java泛型,但如果可能的话,欢迎与概念相关的一般答案。

我知道泛型编程将在编译时处理,而鸭子类型将在运行时处理,但我不知道如何定位它们。

最后,我不想开始辩论,所以我希望得到一些答案,比如支持的理由和反对的理由。

[1] 什么';C++模板和鸭子类型之间的关系是什么?

我遇到了两种不同的"鸭子打字"定义。毫无疑问,有些人会告诉你,其中一个是"正确的",另一个则是"不正确的"。我只是试图记录它们都被使用了,而不是告诉你它们都是"正确的",但就我个人而言,我认为更广泛的含义没有错。

1) 仅运行时键入。类型是对象的属性,而不是变量,因此必然当您对对象调用方法,或以其他方式使用它凭借其类型所具有的属性时,会在运行时发现该方法的存在或不存在[*]。因此,如果它"看起来像鸭子,嘎嘎作响像鸭子"(即,如果它原来有quack()函数),那么它"就是"鸭子(无论如何,你可以像对待鸭子一样对待它)。当然,根据这个定义,C++模板属于第一个障碍,它们使用静态类型。

2) 一个更普遍地用于原则的名称,即如果它看起来像鸭子,嘎嘎作响,那么它就是鸭子,这意味着任何设置中的接口都是由接口的使用者执行的操作隐含地定义的,而不是由生产者显式地宣传的接口(无论实现接口的是什么)。根据这个定义,C++模板确实使用了一种鸭子类型,但某个东西是否"看起来像鸭子"是在编译时而不是运行时根据其静态类型而不是动态类型来确定的。"在编译时检查,这个变量的静态类型会嘎嘎吗?",而不是"在运行时检查,该对象会嘎嘎吗"。

在我看来,争议实际上是关于Python是否"拥有"这个术语,因此只有类似Python的类型系统才能被称为鸭子类型,或者其他人是否可以自由地将这个术语用于不同上下文中的类似概念。无论你认为它应该是什么意思,使用一个"滑稽"的术语并要求每个人都从中理解相同的正式定义似乎是不负责任的。因此,除非有权威的来源,比如词典或任何正式定义它的学术论文,否则不是一个合适的论坛来告诉你一个术语"应该"是什么意思。我想它可以告诉你它的实际含义。

"泛型编程"可以使用duck类型,也可以不使用,这取决于具体的细节。C++通用容器确实使用"类型2"鸭子类型,因为对于C++中的通用类型,它不能保证你可以复制它、比较它、获得哈希值等。Java通用容器没有,Object已经有足够的方法使它可以哈希等。

相反,我怀疑您所做的任何使用鸭子类型的操作都可以合理地称为"泛型编程"。所以我想,用你要求的术语来说,鸭子类型中的GP>DT(任意一种定义)是可以被称为"泛型"的大量内容的严格子集。

[*]好吧,在某些情况下,动态语言可能会有一些静态分析,以某种方式证明情况,但对于静态分析无法得出结论的情况,该语言要求能够将此检查推迟到运行时。

这实际上是一个词汇问题。在最普遍的意义上,泛型编程与编译时与。运行时:这是一个通用问题的解决方案。一个很好的例子泛型编程是运行时是Python,但它也是可能的在C++中实现运行时通用编程(成本高昂在执行时间中)。

Duck类型是一个正交概念,通常用于暗示运行时键入。同样,现代最常被引用的例子是Python,但从Lisp开始的许多语言都在过去。一般来说,C++和Java都明确选择而不是以支持duck类型。这是一种权衡:安全与。灵活性(或编译时错误与运行时错误)。

Java不支持该语言中的鸭子类型。它确实支持可以实现同样目的的反思。据我所见,它与Java的泛型没有任何关系,事实上,让它们一起工作真的很痛苦。

对我来说,"鸭子类型"意味着没有明确的一致性关系。如果某个东西走路像鸭子,说话像鸭子,那么它可以被当作鸭子对待,不需要明确声明它是鸭子。在C++术语中,它不需要从Duck基类继承:继承是一种声明一个类显式地符合另一个类的接口的方式。

这个概念与类型检查是在运行时还是在编译时发生是正交的。像Smalltalk这样的语言提供了在运行时发生的鸭子类型(继承用于重用实现,而不是声明接口的一致性)。C++模板是在编译时发生的一种鸭子类型。

最后一句话就是这个问题的答案。