鸭子打字,它必须是动态的吗?

时间:2009-12-22 17:52:58

标签: language-design duck-typing type-systems definitions structural-typing

维基百科曾经说*关于duck-typing

  

用计算机编程   面向对象的编程语言,   鸭子打字是一种动态的风格   输入一个对象的当前值   一组方法和属性   确定有效的语义   而不是它从特定的继承   特定的类或实现   接口。

(* Ed。注意:自从发布此问题以来,维基百科文章已经过编辑,删除了“动态”一词。)

它说的是structural typing

  

结构型系统(或   基于属性的类型系统)是一个主要的   类型系统的类,在哪种类型   兼容性和等价性   由类型的结构决定,   而不是通过明确的声明。

它将结构子类型与鸭子类型对比如下:

  

[结构系统]与之形成鲜明对比   ...鸭子打字,其中只有   访问的结构的一部分   检查运行时的兼容性。

然而,术语 duck-typing 在我看来至少可以直观地包含结构子类型系统。实际上维基百科说:

  

概念的名称[duck-typing]   是指duck test,归因于   James Whitcomb Riley可能被称为   如下:“当我看到一只走路的鸟   像一只鸭子,像鸭子一样游泳   像鸭子一样嘎嘎叫,我叫那只鸟   鸭“。

所以我的问题是:为什么我不能称为结构子类型鸭子打字?是否存在动态类型语言,这些语言也不能被归类为鸭子类型?

后记:

在reddit.com上有一个名为 daydreamdrunk 的人,所以eloquently put-it“如果它像鸭子一样编译,像鸭子一样链接......”

发表-后记

许多答案似乎基本上只是重复我在这里引用的内容,而没有解决更深层次的问题,这就是为什么不使用术语duck-typing来涵盖动态类型和结构子类型?如果您只想谈论鸭子类型而不是结构子类型,那么只需调用它:动态成员查找。我的问题是, duck-typing 这个词对我说没什么,这只适用于动态语言。

8 个答案:

答案 0 :(得分:26)

C ++和D模板是鸭子打字的一个完美的例子,它不是动态的。绝对是:

  

键入哪个   对象的当前方法集和   属性确定有效   语义,而不是它的继承   从一个特定的类或   具体实施   接口

您没有显式指定类型必须继承的接口来实例化模板。它只需要具有模板定义中使用的所有功能。但是,一切都在编译时得到解决,并编译成原始的,不可思议的十六进制数字。我称之为“编译时鸭子打字”。我已经从这种思维模式编写了整个库,隐式模板实例化是编译时鸭子打字,并认为它是最不受重视的功能之一。

答案 1 :(得分:13)

结构类型系统

结构类型系统将一个完整类型与另一个完整类型进行比较,以确定它们是否兼容。要使AB两种类型兼容,AB必须具有相同的结构 - 即 <{em>上的每个方法{{ 1}}和A必须具有相同的签名。

鸭子打字

鸭子打字认为两种类型等同于用于手头的任务,如果他们都可以处理该任务。对于两种类型BA等同于要写入文件的一段代码,BA都必须实现写入方法。

摘要

结构类型系统比较每个方法签名(整个结构)。 Duck输入比较与特定任务相关的方法(与任务相关的结构)。

答案 2 :(得分:10)

鸭子打字意味着如果它恰好适合,那就没关系

这适用于动态类型

def foo obj
    obj.quak()
end

或静态类型的编译语言

template <typename T>
void foo(T& obj) {
    obj.quak();
}

关键在于,在两个示例中,没有关于给定类型的任何信息。就在使用时(在运行时或编译时!),检查类型,如果满足所有要求,代码就可以工作。值在声明时没有明确的类型

结构类型依赖于明确键入您的值,就像往常一样 - 区别在于具体类型不是通过继承来识别,而是通过它的结构来识别。

上述示例的结构类型代码(Scala样式)将是

def foo(obj : { def quak() : Unit }) {
    obj.quak()
}

不要将此与一些结构类型语言(如OCaml)将其与类型推断相结合,以防止我们明确定义类型。

答案 3 :(得分:6)

我不确定它是否真的能回答你的问题,但是......

模板化的C ++代码看起来非常像鸭子类型,但它是静态的,编译时的,结构化的。

template<typename T>
struct Test
{
    void op(T& t)
    {
        t.set(t.get() + t.alpha() - t.omega(t, t.inverse()));
    }
};

答案 4 :(得分:5)

我的理解是类型推断器等使用结构类型来确定类型信息(想想Haskell或OCaml),而鸭子打字本身并不关心“类型”,只是事物可以处理特定的方法调用/属性访问等(在Ruby中考虑respond_to?或在Javascript中进行能力检查)。

答案 5 :(得分:5)

某些编程语言总会有违反某些术语定义的例子。例如,ActionScript支持在技术上不动态的实例上进行鸭子类型样式编程。

var x:Object = new SomeClass();
if ("begin" in x) {
    x.begin();
}

在这种情况下,我们测试了“x”中的对象实例在调用它之前是否有一个方法“begin”而不是使用接口。这在ActionScript中有效,并且几乎是鸭子类型,即使类SomeClass()本身可能不是动态的。

答案 6 :(得分:4)

在某些情况下,动态鸭子类型和类似的静态类型代码(在C ++中)表现不同:

template <typename T>
void foo(T& obj) {
    if(obj.isAlive()) {
        obj.quak();
    }
}

在C ++中,对象必须同时具有isAlivequak方法才能编译代码;对于动态类型语言中的等效代码,如果quak返回true,则对象只需要isAlive()方法。我将此解释为结构(结构类型)和行为(鸭子类型)之间的差异。

(但是,我通过采取维基百科&#34;达到了这种解释;鸭子打字必须是动态的&#34;面值并试图让它变得有意义。隐式结构打字的另一种解释是鸭子打字也是连贯的。)

答案 7 :(得分:2)

我认为“鸭子打字”更像是一种编程风格,而“结构打字”是一种类型系统特征。

结构类型是指类型系统表达类型的能力,包括具有某些结构属性的所有值。

Duck类型是指编写代码,它只使用传递的值的功能,而这些功能实际上是手头所需的,而不会产生任何其他约束。

所以我可以使用结构类型来编写鸭子打字样式,通过正式声明我的“鸭子类型”作为结构类型。但我也可以使用结构类型而不用“做鸭子打字”。例如,如果我通过声明和命名一个常见的结构类型然后在任何地方使用它来编写接口到一堆相关的函数/ methods / procedures / predicates / classes /的任何东西,很可能一些代码单元不需要所有结构类型的功能,因此我不必要地约束其中一些以拒绝它们理论上可以正常工作的值。

因此,虽然我可以看到有共同点,但我不认为鸭子类型包含结构类型。我想到它们的方式,鸭子打字甚至不是一个可以包含结构类型的东西,因为它们不是同一类型的东西。将动态语言中的鸭子打字视为“隐含的,未经检查的结构类型”的想法遗漏了一些东西,恕我直言。 Duck typing是您选择使用或不使用的编码风格,而不仅仅是编程语言的技术特性。

例如,可以在Python中使用isinstance检查来伪造OO风格的“类或子类”类型约束。还可以检查特定的属性和方法,伪造结构类型约束(您甚至可以将检查放在外部函数中,从而有效地获得命名的结构类型!)。我认为这些选项都不是鸭子打字的例证(除非结构类型非常精细,并且与代码检查保持紧密同步)。