相比于静态类型语言,动态类型语言有哪些优点和限制?
参见: 为什么人们喜欢使用动态语言(一个更加争议的主题...)
相比于静态类型语言,动态类型语言有哪些优点和限制?
参见: 为什么人们喜欢使用动态语言(一个更加争议的主题...)
解释器能够推断类型和类型转换,使得开发时间更快,但也可能引发运行时错误。相比之下,静态类型语言在编译时就能检测到这些错误。然而,哪种方法更好(甚至是否总是正确)已经成为社区内的热门讨论话题(长期以来如此)。
对于这个问题的一个很好的看法来自于微软的Erik Meijer和Peter Drayton所写的《Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages》,可以从这里获得。
支持静态类型的人认为,静态类型的优点包括更早地发现编程错误(例如防止将整数添加到布尔值)、更好的文档形式(例如在解析名称时合并参数的数量和类型)、更多的编译器优化机会(例如,在静态知道接收方的确切类型时用直接调用替换虚拟调用)、增加运行时效率(例如不需要所有值都携带动态类型)以及更好的设计时开发者体验(例如,知道接收器的类型,IDE 可以呈现所有适用成员的下拉菜单)。支持静态类型的狂热者试图让我们相信“类型正确的程序不会出错”。虽然这听起来令人印象深刻,但它是一个相当空洞的陈述。静态类型检查是您程序运行时行为的编译时抽象,因此它必然只是部分正确且不完整的。这意味着由于类型检查器未跟踪的属性,程序仍可能出错,并且有些程序虽然不会出错,但无法进行类型检查。让静态类型变得不那么局限和更加完整的冲动会导致类型系统变得过于复杂和奇特,正如“幻影类型”[11]和“摇晃类型”[10]的概念所示。这就像试图拴上一只球和铁链来跑马拉松,并在第一英里后大喊大叫,即使你放弃了也几乎做到了。PersistentVector
实现使用异构数组而拒绝它。它在运行时工作,但类型系统无法验证它。哦,还有,那些说 "x 类型比 y 类型高效 10 倍" 的人只是在吹牛。在许多情况下,动态类型可能会“感觉”更快,但一旦你实际尝试使你的花哨应用程序运行,它就会失去优势。同样,静态类型可能看起来像是完美的安全网,但是看看 Java 中一些更复杂的通用类型定义,大多数开发人员都会拿到眼罩。即使有类型系统和生产力,也没有银弹。
最后注意:在比较静态和动态类型时不必担心性能。现代 JIT(例如 V8 和 TraceMonkey)已经接近静态语言的性能。此外,Java 实际上编译成一个固有动态中间语言的事实应该是一个提示,对于大多数情况来说,动态类型并不是一些人所说的巨大性能杀手。
dadd
这样的指令是因为它提前知道操作数是double
类型。 - Michael Beer3 + [4,7]
,这是由其类型理论造成的。3 + [4,7]
这样的表达式是有一个明确定义的结果的,即仍然是一个集合。理论上不存在“运行时类型错误”,类型系统在实践中的作用是防止操作对人类来说毫无意义。当然,这些操作仍然只是位的移动和操作。if(1) a = 3; else a = "string";
,显然,由于它总是为真,在程序中else-branch将永远不会被执行,也不会发生类型错误。但它无法以一般方式证明这些情况,因此它被拒绝了。这就是许多静态类型语言的主要缺点,在保护你免受自己的错误时,你也必然会在不需要保护的情况下受到保护。与普遍观念相反,也有按照原则1工作的静态类型语言。它们简单地拒绝所有可以证明会导致类型错误的程序,并通过所有不能证明会导致类型错误的程序。因此,它们允许具有类型错误的程序存在,一个很好的例子是 Typed Racket,它是动态和静态类型的混合体。有人认为这种系统获得了两个世界最好的优点。
静态类型的另一个优势是在编译时已知类型,因此编译器可以使用它。如果我们在 Java 中执行 "string" + "string"
或 3 + 3
,文本中的两个 +
令牌代表完全不同的操作和数据,但编译器仅从类型中就知道选择哪一个。
现在,我要说出一个非常有争议的观点,请耐心听我说:'动态类型'不存在。
听起来非常有争议,但事实如此,从理论上讲,动态类型的语言是无类型的。它们只是具有一种类型的静态类型语言。或者简单地说,它们是由上下文自由语法在实践中生成的语言。
为什么它们没有类型?因为每个操作都被定义并允许在每个操作数上执行。什么是'运行时类型错误'?从理论的角度来看,它只是一个副作用。如果 print("string")
是一个打印字符串的操作,那么 length(3)
也是一个操作,前者的副作用是将string
写入标准输出,而后者只是error: function 'length' expects array as argument.
,就这样。从理论上讲,不存在动态类型语言。它们是无类型的。
好了,'动态类型'语言的明显优点是表达能力强,类型系统只是表达能力的限制。通常情况下,具有类型系统的语言确实会对所有不允许的操作具有定义的结果,如果忽略类型系统,则结果对人类来说毫无意义。许多语言在应用类型系统后失去了图灵完备性。
显而易见的缺点是操作可能会产生对人类来说无意义的结果。为了防止这种情况发生,动态类型语言通常重新定义那些操作,而不是产生无意义的结果,它们重新定义为具有写出错误的副作用,并可能完全停止程序。从理论上讲,这根本不是一个“错误”,事实上,语言规范通常意味着这一点,从理论上讲,这是语言的一种行为,就像打印字符串一样。类型系统因此强制程序员思考代码的流程,以确保这种情况不会发生。或者,确保这种情况发生也可以在某些调试点非常有用,显示这根本不是一个“错误”,而是语言的一个明确定义的属性。实际上,大多数语言都保护了一种遗留的“动态类型”:防止除零。这就是动态类型的含义,没有类型,除了零是与所有其他数字不同的不同类型之外,没有更多的类型。人们所谓的“类型”只是数据的另一个属性,如数组的长度或字符串的第一个字符。许多动态类型语言还允许您编写诸如“错误:此字符串的第一个字符应该是‘z’”之类的内容。另外,为了举一个更荒谬和极端的例子,我目前正在实现一种语言,在这种语言中,“类型”真正作为数组的第一个字符出现,它们是数据,“类型”的数据,即“类型”本身是一种类型和数据,是唯一的数据,它以自身为类型。类型不是静态有限或有界的,但根据运行时信息可以生成新类型。
动态类型最大的“好处”可能是学习曲线较浅。没有需要学习的类型系统,也没有像类型约束这样的非常规语法问题。这使得动态类型对更多人来说更加易于接受,也使其成为许多无法使用复杂静态类型系统的人可行的选择。因此,在教育(例如麻省理工学院的Scheme/Python)和面向非程序员的特定领域语言方面(例如Mathematica),动态类型已经被广泛采用。动态语言也在很少或没有竞争的领域上获得了成功(例如JavaScript)。
最简洁的动态类型语言(例如Perl、APL、J、K、Mathematica)是面向特定领域的,并且在它们设计的领域内可以比最简洁的通用静态类型语言(例如OCaml)更加简洁。
动态类型的主要缺点包括:
运行时类型错误。
实现相同程度的正确性可能非常困难,甚至实际上是不可能的,需要进行更多的测试。
没有编译器验证的文档。
性能较差(通常是在运行时,但有时也会在编译时,例如Stalin Scheme),并且由于依赖复杂的优化,性能很难预测。
个人而言,我从小就使用动态语言,但作为职业人士,除非没有其他可行的选择,否则不会碰它们。
根据Artima的文章Typing: Strong vs. Weak, Static vs. Dynamic:
强类型防止了不同类型之间混合操作,如果要混合类型,则必须使用显式转换
弱类型意味着您可以在不进行显式转换的情况下混合使用不同类型
Pascal Costanza在他的论文 Dynamic vs. Static Typing — A Pattern-Based Analysis (PDF)中指出,在某些情况下,静态类型比动态类型更容易出错。一些静态类型语言要求您手动模拟动态类型才能做到“正确”。这在Lambda the Ultimate上讨论过。
关于静态语言和动态语言有很多不同之处。 对我而言,主要的区别在于,在动态语言中,变量没有固定类型; 相反,类型与值相关联。 因此,在运行时确定执行的确切代码。
在早期或天真的实现中,这会对性能造成巨大的影响,但现代JIT非常接近优化静态编译器的最佳效果(在某些边缘情况下,甚至更好)。
这一切都与选用适当工具有关。没有哪种方法100%更好。两个系统都是人类创造的,而且都有缺陷。抱歉,我们不能制作完美的东西。
我喜欢动态类型,因为它不会干扰我的方式,但是是的,单元错误可能会悄悄地蔓延开来,这是我没有计划的。
相反,静态类型可能会修复前面提到的错误,但会让初学者(在有类型的语言中)疯狂地尝试将一个常量字符和字符串转换。
静态类型: 像Java和Scala这样的语言是静态类型的。
变量必须在代码中定义和初始化后才能使用。
例如: int x; x = 10;
System.out.println(x);
动态类型: Perl 是一种动态类型的语言。
变量在使用之前不需要初始化。
y=10; 在代码的后面部分使用这个变量。