动态类型语言与静态类型语言
与静态类型语言相比,动态类型语言的优点和局限性是什么?
另请参阅:对动态语言的热爱(更具争议性的主题...)
解决方案
解释器推断类型和类型转换的能力使开发时间更快,但是它也可能引发运行时故障,而这些故障是我们无法在编译时捕获它们的静态类型语言中遇到的。但是,这些天来(以及很长一段时间以来),社区中一直在热烈讨论哪个更好(甚至永远都是这样)。
这个问题的一个很好的选择是来自Microsoft的Erik Meijer和Peter Drayton的"可能的静态键入","需要时的动态键入:编程语言之间冷战的终结":
Advocates of static typing argue that the advantages of static typing include earlier detection of programming mistakes (e.g. preventing adding an integer to a boolean), better documentation in the form of type signatures (e.g. incorporating number and types of arguments when resolving names), more opportunities for compiler optimizations (e.g. replacing virtual calls by direct calls when the exact type of the receiver is known statically), increased runtime efficiency (e.g. not all values need to carry a dynamic type), and a better design time developer experience (e.g. knowing the type of the receiver, the IDE can present a drop-down menu of all applicable members). Static typing fanatics try to make us believe that “well-typed programs cannot go wrong”. While this certainly sounds impressive, it is a rather vacuous statement. Static type checking is a compile-time abstraction of the runtime behavior of your program, and hence it is necessarily only partially sound and incomplete. This means that programs can still go wrong because of properties that are not tracked by the type-checker, and that there are programs that while they cannot go wrong cannot be type-checked. The impulse for making static typing less partial and more complete causes type systems to become overly complicated and exotic as witnessed by concepts such as “phantom types” [11] and “wobbly types” [10]. This is like trying to run a marathon with a ball and chain tied to your leg and triumphantly shouting that you nearly made it even though you bailed out after the first mile. Advocates of dynamically typed languages argue that static typing is too rigid, and that the softness of dynamically languages makes them ideally suited for prototyping systems with changing or unknown requirements, or that interact with other systems that change unpredictably (data and application integration). Of course, dynamically typed languages are indispensable for dealing with truly dynamic program behavior such as method interception, dynamic loading, mobile code, runtime reflection, etc. In the mother of all papers on scripting [16], John Ousterhout argues that statically typed systems programming languages make code less reusable, more verbose, not more safe, and less expressive than dynamically typed scripting languages. This argument is parroted literally by many proponents of dynamically typed scripting languages. We argue that this is a fallacy and falls into the same category as arguing that the essence of declarative programming is eliminating assignment. Or as John Hughes says [8], it is a logical impossibility to make a language more powerful by omitting features. Defending the fact that delaying all type-checking to runtime is a good thing, is playing ostrich tactics with the fact that errors should be caught as early in the development process as possible.
摘自Artima的Typing:强vs.弱,静态vs.动态文章:
strong typing prevents mixing operations between mismatched types. In order to mix types, you must use an explicit conversion weak typing means that you can mix types without an explicit conversion
在Pascal Costanza的论文"基于模式的动态和静态键入(PDF)"中,他声称在某些情况下,静态键入比动态键入更容易出错。一些静态类型的语言会强迫我们手动模拟动态类型,以执行"正确的事情"。在Lambda Ultimate上进行了讨论。
关于静态和动态语言,有很多不同的东西。对我来说,主要区别在于动态语言中的变量没有固定的类型。而是将类型绑定到值。因此,直到运行时才确定要执行的确切代码。
在早期或者幼稚的实现中,这会极大地拖累性能,但是现代JIT确实非常接近优化静态编译器所能获得的最佳性能。 (在某些情况下,甚至比这更好)。
静态类型系统寻求静态消除某些错误,在不运行程序的情况下检查程序,并尝试在某些方面证明其可靠性。某些类型的系统比其他类型的系统能够捕获更多的错误。例如,正确使用C可以消除空指针异常,而Java没有这种能力。 Twelf具有一个类型系统,实际上可以保证证明将终止,从而"解决"暂停问题。
但是,没有类型系统是完美的。为了消除特定类别的错误,它们还必须拒绝某些违反规则的完全有效的程序。这就是为什么Twelf并不能真正解决暂停问题的原因,它只是抛出大量完全有效的证明而以奇怪的方式终止,从而避免了该问题。同样,Java的类型系统由于使用异类数组而拒绝Clojure的PersistentVector实现。它可以在运行时运行,但是类型系统无法对其进行验证。
因此,大多数类型系统都提供"转义符",即重写静态检查器的方法。对于大多数语言,这些语言采用强制转换的形式,尽管有些语言(例如Cand Haskell)具有被标记为"不安全"的完整模式。
从主观上讲,我喜欢静态输入。正确地实现(提示:不是Java),静态类型系统可以在错误使生产系统崩溃之前消除错误。动态类型语言倾向于需要更多的单元测试,这在最佳情况下是很乏味的。同样,静态类型的语言可能具有某些特性,这些特性在动态类型系统中是不可能的或者不安全的(隐式转换浮现在脑海)。这都是要求和主观品味的问题。我不会再尝试在Ruby中构建下一个Eclipse,而是尝试在Assembly中编写备份脚本或者使用Java修补内核。
哦,有人说" x打字的效率是y打字的10倍",只是在吹烟。在许多情况下,动态键入可能会"更快"地"感觉",但是一旦我们实际尝试使高档应用程序运行,它就会失去基础。同样,静态类型看上去似乎是完美的安全网,但是对Java中一些更复杂的泛型类型定义的一瞥使大多数开发人员都争先恐后。即使具有类型系统和生产率,也没有灵丹妙药。
最后说明:将静态类型与动态类型进行比较时,不必担心性能。 V8和TraceMonkey之类的现代JIT危险地接近静态语言性能。而且,Java实际上可以编译为固有的动态中间语言这一事实应该表明,在大多数情况下,动态类型并不是某些人认为的巨大性能杀手。
这是完成这项工作的正确工具。 100%的时间都不是更好。两种系统都是由人创造的,并且存在缺陷。抱歉,但是我们很烂并且制作完美的东西。
我喜欢动态类型,因为它会挡住我的注意力,但是是的,运行时错误会蔓延到我本来没有计划的地方。
静态类型可以解决上述错误,但会使初学者(使用类型语言)疯狂地尝试在常量char和字符串之间进行转换。
这取决于上下文。对于动态类型化系统以及强类型化都有很多好处。我认为动态类型语言的流动更快。动态语言不受类属性和编译器对代码中所发生情况的限制。你有一些自由。此外,动态语言通常更具表现力,并且可以减少代码,这是件好事。尽管如此,它更容易出错,这也值得怀疑,并且更多地取决于单元测试的覆盖范围。这是带有动态语言的简单原型,但是维护可能会成为噩梦。
相对于静态类型系统的主要好处是对IDE的支持以及对代码的静态分析器。
每次更改代码后,我们都会对代码更有信心。使用此类工具可以使维护工作轻松自如。
动态打字的最大的"好处"也许就是学习曲线较浅。没有要学习的类型系统,也没有针对诸如类型约束之类的极端情况的非平凡语法。这使得动态类型可供更多人使用,并且对于许多无法使用复杂的静态类型系统的人来说也是可行的。因此,动态类型已经在教育环境(例如MIT的Scheme / Python)和非程序员的领域特定语言(例如Mathematica)中流行起来。动态语言也已经占据了竞争很少或者没有竞争的壁ni(例如Javascript)。
最简洁的动态类型的语言(例如Perl,APL,J,K,Mathematica)是特定于领域的,并且比为它们设计的适当领域中最简洁的通用静态类型的语言(例如OCaml)要简洁得多。 。
动态类型化的主要缺点是:
- 运行时类型错误。
- 要达到相同的正确性可能非常困难,甚至几乎是不可能的,并且需要进行大量测试。
- 没有经过编译器验证的文档。
- 由于依赖复杂的优化,性能不佳(通常在运行时,但有时在编译时,例如斯大林方案),并且性能无法预测。
就我个人而言,我是在动态语言下长大的,但是除非有其他可行的选择,否则我不会以40英尺的杆位作为专业人士来接触它们。
好吧,这两者都是非常非常非常非常容易被误解的东西,也是两个完全不同的东西。不是互相排斥的。
静态类型是语言语法的限制。严格来说,静态类型的语言可以说不是上下文无关的。一个简单的事实是,在上下文无关的语法中明智地表达一种语言变得不方便,而上下文无关的语法不能将其所有数据简单地视为位向量。静态类型系统是语言语法的一部分(如果有的话),它们对上下文的限制要远远超过上下文无关的语法,因此语法检查实际上是在源代码上进行了两次。静态类型对应于类型理论的数学概念,数学中的类型理论只是限制了某些表达式的合法性。就像,我不能在数学上说" 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"写入标准输出,后者只是错误:函数" length"期望数组作为参数。就是这样。从理论的角度来看,没有动态类型的语言之类的东西。他们是无类型的
好吧,"动态类型化"语言的明显优势是表达能力,类型系统不过是表达能力的限制。通常,带有类型系统的语言确实会对所有那些如果仅忽略类型系统而不允许的操作都具有定义的结果,那么该结果对人类来说就没有任何意义。应用类型系统后,许多语言都失去了图灵完整性。
明显的缺点是这样的事实,即会发生可能产生对人类无意义的结果的操作。为了防止这种情况,动态类型化的语言通常会重新定义这些操作,而不是产生无意义的结果,而是将其重新定义为具有产生错误并可能完全停止程序的副作用。这根本不是"错误",实际上,语言规范通常暗示了这一点,从理论的角度来看,这与打印字符串一样,是语言的行为。因此,类型系统迫使程序员对代码流进行推理,以确保不会发生这种情况。甚至确实,在某些方面进行调试的原因也很方便调试,表明它根本不是"错误",而是语言的良好定义。实际上,大多数语言所具有的"动态类型"的唯一残缺在于防止被零除。这就是动态类型,没有类型,没有其他类型,零就是不同于所有其他数字的类型。人们所谓的"类型"只是数据的另一个属性,例如数组的长度或者字符串的第一个字符。许多动态类型语言还允许我们写出诸如"错误:此字符串的第一个字符应为'z'"`之类的内容。
另一件事是,动态类型的语言具有运行时可用的类型,通常可以检查它并对其进行处理并从中确定。当然,从理论上讲,它与访问数组的第一个字符并查看其内容没有什么不同。实际上,我们可以创建自己的动态C,仅使用long long int之类的一种类型,并使用其前8位存储"类型",并相应地编写检查它并执行浮点或者整数加法的函数。我们有一种静态类型的语言或者一种动态语言。
在实践中,所有这些都表明,静态类型的语言通常用于编写商业软件,而动态类型的语言则倾向于用于解决一些问题并自动执行某些任务。用静态类型的语言编写代码只会花费很长时间并且很麻烦,因为我们无法做一些我们知道会变好的事情,但是类型系统仍然可以保护我们免受错误的侵害。许多编码人员甚至没有意识到他们这样做是因为它在系统中,但是当我们使用静态语言进行编码时,通常会遇到以下事实:类型系统不会让我们做不会出错的事情,因为无法证明它不会出错。
正如我指出的那样,"静态类型化"通常意味着情况2,在被证明无罪之前一直是有罪的。但是,某些根本不从类型理论派生其类型系统的语言使用规则1:无罪,直到证明有罪,这可能是理想的混合体。所以,也许Typed Racket适合我们。
同样,对于一个更荒谬和极端的例子,我目前正在实现一种语言,其中"类型"确实是数组的第一个字符,它们是数据,数据本身就是"类型","类型"类型和基准,唯一具有自身作为类型的基准。类型不是有限的或者静态的,但是可以根据运行时信息生成新的类型。