动态类型语言与静态类型语言


205

与静态类型语言相比,动态类型语言的优点和局限性是什么?

另请参阅对动态语言的热爱(更具争议性的主题...)


10
这个问题太主观了。
杰森·达吉特

7
我不会说它是主观的,而是火焰诱饵。但是关于它有一些客观事实。
Vinko Vrsalovic

2
同意:过于主观。比较和对比这两种方法很有趣,但是它濒临论坛启示录的边缘。
Daniel Spiewak

17
动态语言非常适合快速开发演示/废弃应用程序,因为如果您打错了打字员,网页仍然会加载,那么您可能在这里或那里只有几个数据元素错误。我无法想象在任何其他情况下将变量键入错误而又不会出现编译器错误的情况被视为“优势”。
亚历克斯R

4
这种错误通常会使JavaScript停顿下来,我认为这是一件非常好的事情。至少它会引发一些错误,我也认为这些错误很有价值。出于某种原因,它总是一个来自静态类型输入法的人,他想用空的try / catch语句掩埋其JavaScript错误。根据我的经验,这是一种现象。那是什么?无论如何,这并不是说我们在运行代码时没有得到反馈。
Erik Reppen

Answers:


139

解释器推断类型和类型转换的能力使开发时间更快,但是它也可能引发运行时失败,而您在静态类型语言中就无法遇到这种情况,而您只能在编译时捕获它们。但是,这些天来(以及很长一段时间以来),社区中一直在热烈讨论哪个更好(甚至永远都是这样)。

这个问题的一个很好的选择是来自Microsoft的Erik Meijer和Peter Drayton的“ 可能的静态键入”,“需要时的动态键入:编程语言之间冷战的终结”

静态类型的提倡者认为,静态类型的优点包括更早地发现编程错误(例如,防止在布尔值中添加整数),以类型签名的形式提供更好的文档(例如,在解析名称时合并参数的数量和类型),更多编译器优化的机会(例如,当静态地知道接收者的确切类型时,用直接调用替换虚拟调用),提高的运行时效率(例如,并非所有值都需要携带动态类型)以及更好的设计时开发人员经验(例如,了解根据接收器的类型,IDE可以显示所有适用成员的下拉菜单)。静态打字狂热者试图使我们相信“打字正确的程序不会出错”。虽然这听起来确实令人印象深刻,这是一个空洞的声明。静态类型检查是对程序运行时行为的编译时抽象,因此它必然仅是部分健全和不完整的。这意味着由于类型检查器未跟踪的属性,程序仍然可能出错,并且有些程序即使出错也无法进行类型检查。诸如使“静态类型” [11]和“摇摆类型” [10]之类的概念所见证的,使静态类型变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上进行马拉松比赛,并欢呼地喊道,即使在第一英里之后获得了保释,您还是差一点就成功了。静态类型检查是对程序运行时行为的编译时抽象,因此它必然仅是部分健全和不完整的。这意味着由于类型检查器未跟踪的属性,程序仍然可能出错,并且有些程序即使出错也无法进行类型检查。诸如使“静态类型” [11]和“摇摆类型” [10]之类的概念所见证的,使静态类型变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上进行马拉松比赛,并欢呼地喊道,即使在第一英里之后获得了保释,您还是差一点就成功了。静态类型检查是对程序运行时行为的编译时抽象,因此它必然仅是部分健全和不完整的。这意味着由于类型检查器未跟踪的属性,程序仍然可能出错,并且有些程序即使出错也无法进行类型检查。诸如使“静态类型” [11]和“摇摆类型” [10]之类的概念所见证的,使静态类型变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上进行马拉松比赛,并欢呼地喊道,即使在第一英里之后获得了保释,您还是差一点就成功了。因此,它必然只是部分健全和不完整。这意味着由于类型检查器未跟踪的属性,程序仍然可能出错,并且有些程序即使出错也无法进行类型检查。诸如使“静态类型” [11]和“摇摆类型” [10]之类的概念所见证的,使静态类型变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上进行马拉松比赛,并欢呼地喊道,即使在第一英里之后获得了保释,您还是差一点就成功了。因此,它必然只是部分健全和不完整。这意味着由于类型检查器未跟踪的属性,程序仍然可能出错,并且有些程序即使出错也无法进行类型检查。诸如使“静态类型” [11]和“摇摆类型” [10]之类的概念所见证的,使静态类型变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上进行马拉松比赛,并欢呼地喊道,即使在第一英里之后获得了保释,您还是差一点就成功了。并且有些程序虽然不能出错也不能进行类型检查。如“幻像类型” [11]和“摇摆类型” [10]等概念所见证的那样,使静态类型化变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上的马拉松赛跑,并欢呼地喊道,即使您在第一英里后获得了保释,也差一点就成功了。并且有些程序虽然不能出错也不能进行类型检查。诸如使“静态类型” [11]和“摇摆类型” [10]之类的概念所见证的,使静态类型变得不那么局部而更加完整的冲动导致类型系统变得过于复杂和奇特。这就像在尝试用绳子和链子绑在腿上的马拉松赛跑,并欢呼地喊道,即使您在第一英里后获得了保释,也差一点就成功了。

动态类型语言的拥护者认为,静态类型过于僵化,动态语言的柔和性使其非常适合于具有变化或未知需求的原型系统,或与无法预测的其他系统(数据和应用程序集成)交互的系统。当然,动态类型的语言对于处理真正的动态程序行为(例如方法拦截,动态加载,移动代码,运行时反射等)是必不可少的。在有关脚本的所有论文之母[16]中,John Ousterhout认为静态类型的系统与动态类型的脚本语言相比,编程语言使代码的可重用性更低,更冗长,更不安全且更具表现力。实际上,许多类型的动态类型化脚本语言的支持者都对这个论点提出了质疑。我们认为这是一个谬论,并且与声明式编程的本质是消除赋值的争论属于同一类。或如约翰·休斯(John Hughes)所说[8],通过省略功能使语言更强大是一种逻辑上的不可能。捍卫将所有类型检查延迟到运行时是一件好事的事实,这是一种愚蠢的策略,其事实是应在开发过程中尽早发现错误。通过省略功能来使语言更强大是逻辑上不可能的。捍卫将所有类型检查延迟到运行时是一件好事的事实,这是一种愚蠢的策略,其事实是应在开发过程中尽早发现错误。通过省略功能来使语言更强大是逻辑上不可能的。捍卫将所有类型检查延迟到运行时是一件好事的事实,这是一种愚蠢的策略,其事实是应在开发过程中尽早发现错误。


37
仅出于记录目的,“方法拦截,动态加载,移动代码,运行时反射”都可以用Java完成。
Will Hartung

13
幻像类型并不“过于复杂”。
乔恩·哈罗普

12
截至2010年5月16日,指向Meijer论文的链接已断开。
jchadhowell,2010年


4
@VinkoVrsalovic具有类型推断和多态性的静态语言非常适合快速原型制作。它们提供与动态语言相同的舒适性和静态语言的安全性。
Edgar Klerks

119

静态类型系统寻求静态消除某些错误,在不运行程序的情况下检查程序,并尝试在某些方面证明其可靠性。某些类型的系统比其他类型的系统能够捕获更多的错误。例如,正确使用C#可以消除空指针异常,而Java没有这种能力。Twelf具有一个类型系统,实际上可以保证证明将终止,从而“解决” 暂停问题

但是,没有类型系统是完美的。为了消除特定类别的错误,它们还必须拒绝某些违反规则的完全有效的程序。这就是为什么Twelf不能真正解决暂停问题的原因,它只是抛出大量完全有效的证明而以奇怪的方式终止,从而避免了该问题。同样,Java的类型系统PersistentVector由于使用异类数组而拒绝Clojure的实现。它在运行时有效,但是类型系统无法验证它。

因此,大多数类型系统都提供“转义符”,即覆盖静态检查器的方法。对于大多数语言,它们采用强制转换的形式,尽管某些语言(例如C#和Haskell)具有标记为“不安全”的整个模式。

从主观上讲,我喜欢静态键入。正确地实现(提示:不是 Java),静态类型系统可以在错误使生产系统崩溃之前清除错误。动态类型的语言往往需要更多的单元测试,这在最佳情况下是很乏味的。同样,静态类型的语言可能具有某些特性,这些特性在动态类型系统中是不可能的或不安全的(隐式转换浮现在脑海)。这都是要求和主观品味的问题。与尝试在Assembly中编写备份脚本或使用Java修补内核一样,我将不会再在Ruby中构建下一个Eclipse。

哦,有人说“ x打字的效率是y打字的10倍”,只是在吹烟。在许多情况下,动态键入可能“感觉”得更快,但是一旦您实际尝试使精美的应用程序运行,它就会失去基础。同样,静态类型看似似乎是完美的安全网,但一看Java中一些更复杂的泛型类型定义,大多数开发人员就会争先恐后。即使具有类型系统和生产率,也没有灵丹妙药。

最后说明:将静态类型与动态类型进行比较时,不必担心性能。V8和TraceMonkey之类的现代JIT危险地接近静态语言性能。而且,Java实际上可以编译为固有的动态中间语言这一事实​​应该表明,在大多数情况下,动态类型并不是某些人认为的巨大性能杀手。


5
关于表演。在通常情况下,差异不会太大,但是在高压数学等方面,确实存在差异。在ipy与C#的情况下,Test证明了对函数的调用,相差一千个周期。仅仅因为前者必须确保存在一种方法。
Dykam,

29
您能否详细说明“正确使用C#可以消除空指针异常,而Java没有这种能力”。?一个例子或引用将不胜感激。
斯里尼瓦斯·雷迪·塔蒂帕西2011年

13
“ Java中的一些更复杂的泛型类型定义使大多数开发人员都
争相盲目地瞎眼

3
“而且,Java实际上可以编译为固有的动态中间语言这一事实​​应该表明,在大多数情况下,动态类型并不是某些人认为的巨大性能杀手。” -当您的具有“良好性能”的语言示例是Java时,您可能需要重新考虑。
Yakk-Adam Nevraumont 2014年

Java编译为固有的动态中间件 ”-遗漏了重点。静态检查已经预先执行,因此,dadd因为编译器选择指令(例如因为他事先知道操作数为doubles),所以不需要覆盖这些检查的其他运行时检查。
Michael Beer '18

45

好吧,这两者都是非常非常非常非常容易被误解的东西,也是两个完全不同的东西。不是互相排斥的

静态类型是语言语法的限制。严格来说,静态类型的语言可以说不是上下文无关的。一个简单的事实是,在上下文无关的语法中明智地表达一种语言变得不方便,而上下文无关的语法不能将其所有数据简单地视为位向量。静态类型系统是语言语法的一部分(如果有的话),它们对上下文的限制要远远超过上下文无关的语法,因此语法检查实际上是在源代码的两次遍历中进行的。静态类型对应于类型理论的数学概念,数学中的类型理论仅限制了某些表达式的合法性。就像,我不能3 + [4,7]在数学上说,这是因为它的类型论。

因此,从理论上讲,静态类型不是“预防错误”的方法,它们是语法的限制。的确,只要+,3和interval具有通常的集合理论定义,如果我们删除类型系统,3 + [4,7]则其定义的结果很好。理论上不存在“运行时类型错误”,类型系统的实际用途是防止对人类没有意义的操作。当然,操作仍然只是位的移位和操纵。

要注意的是,类型系统无法确定是否将要执行此类操作。如图所示,将所有可能程序的集合准确地划分为那些将具有“类型错误”的程序和没有“类型错误”的程序。它只能做两件事:

1:证明类型错误将在程序中发生
2:证明它们不会在程序中发生

看来我在矛盾自己。但是C或Java类型检查器的作用是将程序拒绝为“不合语法的”,或者将其称为“类型错误”,如果该程序无法在2点成功执行。它无法证明它们不会发生,这并不意味着它们不会发生,仅意味着它无法证明这一点。很可能是因为没有编译器无法证明没有类型错误的程序而被拒绝。一个简单的例子是if(1) a = 3; else a = "string";,当然,因为它始终为true,所以else分支将永远不会在程序中执行,并且不会发生类型错误。但是它不能以一般的方式证明这些情况,因此被拒绝了。这是许多静态类型语言的主要弱点,在保护您免受自身侵害的情况下,在您不需要它的情况下也一定要保护您。

但是,与流行的看法相反,也有一些静态类型化的语言按原则1起作用。它们只是拒绝所有可以证明将导致类型错误的程序,并通过所有它们不能通过的程序。因此,它们可能允许其中存在类型错误的程序,例如Typed Racket是一个很好的例子,它是动态类型和静态类型之间的混合体。有人会争辩说,您在此系统中两全其美。

静态类型化的另一个优点是类型在编译时是已知的,因此编译器可以使用它。如果我们用Java做"string" + "string"3 + 3+最后文本中的两个标记都代表完全不同的操作和数据,则编译器知道仅从类型中进行选择即可。

现在,我将在这里做一个非常有争议的声明,但请允许我:'动态类型'不存在

听起来很有争议,但确实如此,从理论上讲,动态类型化语言是未类型化的。它们只是只有一种类型的静态类型语言。或简单地说,它们是实际上由上下文无关语法在语法上生成的语言。

他们为什么没有类型?因为每个操作都定义并允许每个操作,所以“运行时类型错误”到底是什么?这纯粹是从理论上的一个副作用。如果执行print("string")打印字符串操作是操作,则也是如此length(3),前者具有写入string标准输出的副作用,后者简单地error: function 'length' expects array as argument.就是这样。从理论的角度来看,没有动态类型的语言。他们是无类型的

好吧,“动态类型化”语言的明显优势是表达能力,类型系统不过是表达能力的限制。通常,对于带有类型系统的语言,如果只是忽略类型系统,那么对于所有那些不允许的操作,确实会有定义好的结果,结果对于人类来说将是毫无意义的。应用类型系统后,许多语言都失去了图灵完整性。

明显的缺点是这样的事实,即会发生可能产生对人类无意义的结果的操作。为了避免这种情况,动态类型化的语言通常会重新定义这些操作,而不是产生无意义的结果,而是将其重新定义为具有写出错误并可能完全停止程序的副作用。这根本不是“错误”,实际上,语言规范通常暗示了这一点,从理论的角度来看,这与打印字符串一样,是语言的行为。因此,类型系统迫使程序员对代码流进行推理,以确保不会发生这种情况。或者实际上,之所以这么说是在某些方面进行调试也很方便,这表明它根本不是“错误”,而是语言的良好定义。实际上,大多数语言所具有的“动态类型”的唯一残缺在于防止被零除。这就是动态类型,没有类型,没有其他类型,零是与所有其他数字不同的类型。人们所谓的“类型”只是数据的另一个属性,例如数组的长度或字符串的第一个字符。许多动态类型语言还允许您编写类似的内容"error: the first character of this string should be a 'z'"

另一件事是,动态类型的语言具有在运行时可用的类型,通常可以检查它并对其进行处理并从中进行决策。当然,从理论上讲,它与访问数组的第一个字符并查看其内容没有什么不同。实际上,您可以创建自己的动态C,仅使用long long int之类的一种类型,并使用其前8位存储“类型”,并相应地编写检查和执行浮点或整数加法的函数。您有一种静态类型的语言或一种动态语言。

在实践中,这一切都表明,静态类型的语言通常用于编写商业软件,而动态类型的语言则倾向于用于解决一些问题并使某些任务自动化的环境。用静态类型的语言编写代码只会花费很长时间并且很麻烦,因为您无法做一些您知道会变好的事情,但是类型系统仍然可以保护您免受错误的侵害。许多编码人员甚至没有意识到他们这样做是因为它在系统中,但是当您使用静态语言进行编码时,您经常会遇到以下事实:类型系统不会让您做不会出错的事情,因为它无法证明它不会出错。

正如我指出的那样,“静态类型化”通常意味着情况2,在被证明无罪之前一直有罪。但是,某些根本不从类型理论派生其类型系统的语言会使用规则1:在证明有罪之前是无辜的,这可能是理想的混合体。所以,也许Typed Racket适合您。

同样,对于一个更荒谬和极端的例子,我目前正在实现一种语言,其中“类型”确实是数组的第一个字符,它们是数据,数据本身就是“类型”,“类型”类型和基准,唯一具有自身作为类型的基准。类型不是有限的或静态的,但是可以根据运行时信息生成新的类型。


1
“许多语言在应用类型系统后都失去了图灵完整性。” 不适用于通常的编程语言,对吗?从我读,正规语言不是图灵完备的
的Răzvan弗拉菲乌斯熊猫

1
@RăzvanPanda:Lajla可能是指Typed lambda演算的变体或定理证明者使用的某些编程语言。这些程序中的许多程序只能表示保证可以停止的程序,因此不能完成图灵的程序。基于这些类型系统的实用函数式编程语言通过使用递归类型扩展核心演算来解决此限制。
hugomg

1
“声音争议很大,但确实如此,从理论上讲,动态类型化语言是无类型的。” -...而且我马上就知道您不知道您在说什么。动态类型只是意味着类型属于值,而不是标识符。它使程序难以证明,但不一定是不可能的。内联和参数多态性已经导致链接时间优化的发展。它解决了编译最佳动态类型语言所具有的相同类型的问题:知道所有可能的输入和输出。
DeftlyHacked

25

动态打字的最大的“好处”也许就是学习曲线较浅。没有学习类型系统,也没有针对特殊情况(例如类型约束)的非平凡语法。这使得动态类型可供更多人使用,并且对于许多无法使用复杂的静态类型系统的人来说也是可行的。因此,动态类型已经在教育(例如MIT的Scheme / Python)和非程序员的领域特定语言(例如Mathematica)的环境中流行起来。动态语言也已经占据了竞争很少或没有竞争的壁ni(例如Javascript)。

最简洁的动态类型化语言(例如Perl,APL,J,K, Mathematica)是特定于领域的,并且在其专门设计的领域中比最简洁的通用静态类型化语言(例如OCaml)更为简洁。 。

动态类型化的主要缺点是:

  • 运行时类型错误。

  • 要达到相同的正确性可能非常困难,甚至几乎是不可能的,并且需要进行大量测试。

  • 没有经过编译器验证的文档。

  • 由于依赖复杂的优化,性能较差(通常在运行时,但有时在编译时,例如,斯大林方案),并且性能不可预测。

我个人是在动态语言下长大的,但除非有其他可行的选择,否则我不会以40英尺的杆位触动他们。


2
我会说较低的进入门槛,但精通同样是学习曲线。
Erik Reppen 2013年

学习曲线难道不是因为没有要学习的类型系统而减少吗?
乔恩·哈罗普

还有一个类型系统。您可以对添加布尔值和字符串时会发生的情况做出合理的猜测,但是了解动态类型语言中如何强制类型的一些实际细节通常会很有帮助。这是很多严格的人所不具备的。我们实际上学习了这些东西。
Erik Reppen '18

@ErikReppen:我们正在使用“类型系统”的不同定义。我指的是不必学习静态类型系统,例如代数数据类型,泛型。您所指的“类型”只是数据。一些函数在运行时拒绝某些数据这一事实是普遍的。
乔恩·哈罗普

12

摘自Artima的打字:强vs.弱,静态vs.动态文章:

强类型可防止不匹配类型之间的混合操作。为了混合类型,必须使用显式转换

弱类型意味着您无需显式转换即可混合类型

在Pascal Costanza的论文中, 动态与静态打字-基于模式的分析(PDF)》中,他声称在某些情况下,静态打字比动态打字更容易出错。一些静态类型的语言会强迫您手动模拟动态类型,以执行“正确的事情”。在 Lambda Ultimate上进行了讨论。


1
“静态类型比动态类型更容易出错”-是的,是的,并且是双重的!我在这两种语言上都有丰富的经验,在每种情况下,动态语言都“起作用”,而静态语言则需要2倍的调试时间(请参见C ++和Delphi)。这通常是由于类型问题引起的,尤其是在带有疯狂类型的模块和函数之间传递数据。即使动态语言可能会引起各种各样的理论错误,但实际上,除非您是一个滥用动态类型的程序员,否则我很少遇到类型强制导致的错误。
dallin 2013年

7
几年前,我读了一份Costanza论文草稿。在他写过“ static”的任何地方,他的意思都是“ Java”。我用OCaml等语言给他提供了数十个反例,这些例子反驳了他的主张,但他还是继续发表了。从那张纸的样子看,他仍然在发表同样的废话。例如,在那篇论文中,他声称“ C#通常是Java的不良副本”。在科学论文中没有任何位置……
乔恩·哈罗普

@dallin我的经历是完全相反的:必须使用C,C ++,Java,Python,Perl等进行大量编程,除非强制执行,否则我不会使用动态类型语言启动比小型调整程序更大的程序。在Python中,考虑WSGI项目时,我仍然不寒而栗:我不得不重写的回调在对象的引用中传递,并且当崩溃时,代码似乎运行良好,因为事实证明有时它不是对象,而是某些元素类型被通过。一种很容易创建这样的漏洞的语言,这很危险。
Michael Beer '18

@MichaelBeer您还可以说像C / C ++这样的语言直接让您管理内存是很危险的!我肯定已经为数小时的内存错误而苦苦挣扎。庞大的Java项目也不是野餐。用任何一种语言,您都必须了解这种语言的危险和良好做法。我曾经从事过的最糟糕的项目是团队PHP项目,它们的结构很少,但是我也从事过使用动态语言的项目,当他们使用良好的框架和良好的编程习惯时,这就是他们的梦想。
达林

@dallin同意,每种语言都有其陷阱。但是我提到的缺陷是任何动态类型语言都固有的,直接操作内存的可能性不是静态类型语言的固有特性。您可以想象动态类型化的语言可以让您直接操作记忆。我同意C ++是一场灾难,该语言的发明者本人相信,在这个星球上,没有一个人能够了解该语言的所有部分。但是,这不能怪C ++是静态类型,而是怪兽自30年以来一直在增长……
迈克尔·比尔

3

这取决于上下文。对于动态类型化系统以及强类型化都有很多好处。我认为动态类型语言的流动更快。动态语言不受类属性和编译器对代码中所发生情况的限制。你有一些自由。此外,动态语言通常更具表现力,并且可以减少代码,这是件好事。尽管如此,它更容易出错,这也值得怀疑,并且更多地取决于单元测试的覆盖范围。这是带有动态语言的简单原型,但维护可能会成为噩梦。

相对于静态类型系统的主要好处是对IDE的支持以及对代码的静态分析器。每次更改代码后,您都会对代码更有信心。使用此类工具可以使维护工作轻松自如。


0

关于静态和动态语言,有很多不同的东西。对我来说,主要区别在于动态语言中的变量没有固定的类型。而是将类型绑定到值。因此,要确定执行的确切代码,直到运行时为止。

在早期或幼稚的实现中,这会极大地拖累性能,但现代JIT确实接近优化静态编译器所能达到的最佳性能。(在某些情况下,甚至比这更好)。


0

这是完成这项工作的正确工具。100%的时间都不是更好。这两个系统都是由人创造的,并且存在缺陷。抱歉,但是我们吸吮并制作出完美的东西。

我喜欢动态类型,因为它会挡住我的注意力,但是是的,运行时错误会蔓延到我本来没有计划的地方。静态类型可以解决上述错误,但会使初学者(使用类型化语言)疯狂地尝试在常量char和字符串之间进行强制转换。


-3

静态打字: Java和Scala等语言是静态类型的。

在代码中使用变量之前,必须先定义和初始化变量。

对于前。int x; x = 10;

System.out.println(x);

动态打字: Perl是一种动态类型的语言。

在代码中使用变量之前,无需对其进行初始化。

y = 10;在代码的后面部分使用此变量


5
这与类型系统无关。
Thiago Negri 2013年
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.