渐进式打字:“几乎所有具有静态类型系统的语言也都具有动态类型系统”


20

这要求阿莱克斯布罗姆菲尔德指出:

几乎所有带有静态类型系统的语言都具有动态类型系统。除了C,我想不到一个例外

这是有效的主张吗?我知道在运行时使用反射或加载类,Java会有点像这样-但是,这种“渐进式输入”的思想可以扩展到多种语言吗?


我不是语言专家,但是我马上想到一些我不相信它们具有动态类型的方法(当然,并不是说您无法将这些类型融合在一起)
-Fortran

4
我是语言专家,我不确定这个人在说什么。“静态”和“动态”是错误的名词,通常指示类型检查/分析的时间(无论是在编译时还是在运行时)。某些语言可以两者兼顾(例如,通过在编译过程中禁止对不受支持的类型进行操作,并在运行时引发诸如ClassCastException之类的异常),这也许就是他的意思。如果是这样,那么C通常在运行时通常不会执行类型检查。说一个郎。没有“动态类型系统”没有任何意义。
Thiago Silva

Answers:


37

原始高音扬声器在这里。:)

首先,我对自己的推文被如此认真地感到很开心/震惊!如果我知道它将被广泛传播,那么我将花费超过30秒钟来编写它!

Thiago Silva是正确的指出,“静态”和“动态”更准确地描述了类型检查,而不是类型系统。实际上,说一种语言是静态还是动态类型也不是很准确。而是,一种语言具有类型系统,并且该语言的实现可能会使用静态检查或动态检查,或同时使用这两种方法或两者都不执行(尽管这不是非常吸引人的语言实现!)来强制执行类型系统。

碰巧的是,某些类型系统(或类型系统的特征)更适合于静态检查,并且某些类型系统(或类型系统的特征)更适合于动态检查。例如,如果您的语言允许您在程序的文本中指定特定值必须始终是整数数组,那么编写静态检查器以验证该属性相当合理。相反,如果您的语言具有子类型,并且允许向下转换,则在运行时检查向下转换的有效性相当简单,但在编译时则非常困难。

我真的被我的鸣叫的意思很简单,就是绝大多数语言实现的执行一些动态类型检查的数量。或者,等效地,绝大多数语言都具有一些难以(如果不是不可能)进行静态检查的功能。向下转换就是一个例子。其他示例包括算术溢出,数组边界检查和null检查。其中有些可以在某些情况下进行静态检查,但是总的来说,您很难找到在运行时不进行任何检查的语言实现。

这不是一件坏事。只是观察到,我们希望我们的语言强制执行许多有趣的属性,而我们实际上并不知道如何进行静态检查。提醒您,“静态类型”与“动态类型”之类的区别并不像某些人所希望的那样清晰。:)

最后一点:编程语言研究社区中并未真正使用术语“强”和“弱”,并且它们的含义并不一致。总的来说,我发现当有人说某种语言具有“强类型”而另一种语言具有“弱类型”时,他们实际上是在说自己喜欢的语言(具有“强类型”的一种)会阻止他们犯了一个错误,即另一种语言(“弱类型”的一种)不会-或者相反,他们最喜欢的语言(“弱类型”的一种)使他们做一些很酷的事情,而另一种语言(带有“强类型”的关键字)。


9
“这只是一个观察,我们希望我们的语言能够强制执行许多有趣的属性,而且我们真的不知道如何进行静态检查。” –好吧,不仅仅是我们不知道该怎么做。“难道此输入该程序暂停”是一个有趣的特性,我们不只是不知道如何检查,但事实上我们知道是不可能的检查。
约尔格W¯¯米塔格

“其他示例包括算术溢出,数组边界检查和空检查。” 请注意,虽然有几种语言在类型系统中检查这些属性,但即使在大多数动态类型的语言中,这些语言通常也是的功能,而不是类型。(不过,“空”检查是静态类型系统的常见功能。)
porglezomp 2015年

这里一个动态的类型系统和静态类型系统。即使没有强制执行,它们仍然存在,描述什么是正确的,什么是错误。语言/实现可能会也可能不会检查它们中的任何一个。顺便说一句,静态和动态类型系统应保持一致,但从定义上来看并非如此。
Elazar

每种语言都有一个动态类型系统,该系统是禁止执行某些操作的一组规则。
Elazar

7

嗯,是。您可以使用任何静态类型的语言来放置财产袋。语法将很糟糕,同时您将获得动态类型系统的所有缺点。因此,除非编译器允许您使用更好的语法(如C#dynamic所做的事情),否则实际上并没有任何优势。

同样,您也可以在C中轻松完成该操作。

对其他答案的反应:我认为人们将静态/动态输入与强/弱输入相混淆。动态类型化是关于能够在运行时更改数据的结构,以及使代码能够使用恰好符合代码需求的数据。这就是所谓的Duck Typing

提及反射并不能说明全部问题,因为反射不允许您更改数据的现有结构。您不能使用C,C ++,Java或C#将新字段添加到类或结构中。在动态语言中,不仅可以向现有类添加新的字段或属性,而且实际上很常见。

例如,查看Cython,这是Python到C的编译器。它创建静态C代码,但类型系统仍保留其动态特性。C是静态类型的语言,但它能够支持来自Python的动态类型。


从技术上讲,您可以使用ExpandoObject,从版本4开始使用C#,尽管它是一个选择性的过程,与JavaScript或Ruby截然不同。不过,您已经提出了非常重要的一点,那就是鸭子输入(99%的开发人员在说“动态类型”时实际上意味着什么)和反射根本不是一回事。
2013年

可能我还要补充一点,Python没有真正的鸭子类型。它只有一些钩子供您“实现自己的”(有一个魔术方法可以返回True,以说“这个疯狂的对象是我正在定义的类的实例”)。据我了解,OCaml确实具有此功能,但我真的不知道。
vlad-ardelean 2014年

6

动态语言是静态语言。通常所谓的“动态类型”实际上是静态类型的一种特殊情况-这种情况是您将自己限制为仅具有一种类型。作为一个思想实验,想象一下Object在调用任何方法之前,仅使用变量/字段/参数编写Java或C#程序,并向下转换。将像Python或Javascript这样的语言称为“ unityped”会更准确。(这种说法可能会使许多人感到困惑/烦恼,考虑到这样的Java或C#程序将使用许多子类型,但这是因为普通的OOP语言会压缩类型和类。有关更多详细信息,请参阅博客文章。)

请注意,即使C也具有“动态”类型-您可以将任何指针转换为void(如果有内存,则可以char)指针,然后再次返回。还要注意,那里没有运行时检查。如果您弄错了,请享受您未定义的行为!


但是演员表是静态完成的。我看不到这是动态类型的例子。
osa

@osa只是因为代码说的String foo = (String) bar并不意味着bar实际上是一个String。您只能在运行时肯定知道这一点,所以我看不到投射是如何“静态”完成的。
Doval 2014年

我不同意这一点。至少在Javascript中,您可以在运行时向对象添加新的方法和属性。在C#中,您需要显式使用一个dynamic对象来执行此操作。如果您尝试向...添加属性object,那您就不能。
Arturo TorresSánchez16年

3

静态和动态类型之间的差别是一个值的类型检查:编译时间与运行时间。在带有类型的语言(例如Java对象)中,即使语言实际上更喜欢静态类型,您也可以始终诉诸于动态类型。这是Java中带有动态类型化方法的示例:

void horribleJava(List untypedList) {
  for (Object o : untypedList)
    ((SpecificType) o).frobnicate();
}

注意如何在运行时检查每个项目的类型。等效的静态类型方法为:

void goodJava(List<SpecificType> typedList) {
  for (SpecificType o : typedList) {
    o.forbnicate();
  }
}

在C语言中,值(尤其是指针)在运行时不会保留其类型-每个指针都等效于a void *。相反,变量和表达式具有类型。为了实现动态类型,您必须自己携带类型信息(例如,作为结构中的字段)。


1
从任何实际意义上讲,我都不认为演员表算作动态类型。它仍然需要在编译时了解类型,只是将实际验证推迟到运行时。frobnicate在不了解的情况下,您无法在此处调用该方法SpecificType
亚罗诺(Aaronaught)2013年

2
@Aaronaught但这动态类型,一旦我们将类型检查推迟到运行时。请注意,我的两个示例都使用了标称类型,这需要特定的类型名称。您正在考虑结构化类型,例如需要某种方法的鸭子类型。轴的结构性,名义性和静态性与动态类型是相互正交的(Java是名词性的,并且大多数是静态的; ML和Go是结构性的和静态的; Perl,Python和Ruby的(主要是结构性的和动态的))。
阿蒙

就像我在上一条评论中所说的那样,这是理论上的区别,我从未见过的程序员真的不在乎。您可以粗暴地辩称人们使用了错误的术语(实际上似乎确实是原始的高音扬声器正用于这种贪睡的行为),但对于实际上处于困境中的人们而言,动态打字=鸭子打字。实际上,定义是如此普遍,以至于诸如C#之类的语言实际上已经将其编码(即使用dynamic关键字)。将静态等同于编译时,将动态等同于运行时几乎只会使事情变得混乱。
亚罗诺(Aaronaught)2013年

@Aaronaught:如果将一个类型转换为一个接口,并且如果实现具有预期含义的方法的类始终实现相同的接口这样说,则从本质上讲就是运行时鸭子输入。尽管有些人可能会认为“鸭子类型”应仅使用方法名称,但我认为将“真实”方法名称视为接口名称加方法名称会更有用。否则,应该[1,2].Add([3,4])产生[1,2,3,4][1,2,[3,4]][4,6]
超级猫

@supercat:以上都不是,它应该失败,因为Addarray上没有任何方法可以接受数组,因为这样的方法会模棱两可。鸭子打字不会使您无法理解各种类型和功能。
Aaronaught

2

静态类型与动态类型基本上是指如何检查类型。静态类型表示基于实际代码(通常由编译器)检查各种变量或表达式类型的验证,而在动态类型系统中,此验证仅在运行时由运行时环境执行。

我认为本文所指的是,即使实际上是静态检查类型,也要在运行时(即动态检查)类型。您正确地提到了Java Reflection;反射仅在运行时发生,而Java运行时环境(JVM)实际上在使用反射时执行类型检查,这基本上意味着动态类型检查。


那是不正确的。在C ++,Pascal,Fortran中,使用任何已编译的语言,这些类型仅是静态检查的,而不是动态检查的(除非您使用dynamic_cast)。您可以使用指针将任意内容写入内存,并且没人会知道类型。因此,静态类型表示仅进行静态检查,而动态类型表示仅在运行时检查。
osa

-1

排错是错误的:C也有重要的动态类型系统。它只是不检查它(“ C是强类型,弱检查”)。例如,将结构视为doublereinternpret_cast-style)会产生未定义的行为-动态类型错误。


(对于那些投票否决的人-@Thiago Silva的评论说的是同一件事)
Elazar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.