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

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

另请参见 :动态语言的爱是什么(一个更有争议的线程......)


解释器推导出类型和类型转换的能力使得开发时间更快,但是它也可能引发运行时失败,而在编译时你无法获得静态类型的语言。 但是,哪一个更好(或者哪怕始终如此)在当今社区(以及很长一段时间)都在社区中热烈讨论。

关于这个问题的一个很好的建议是,尽可能使用静态分类,当需要时动态分类:编程语言之间的冷战结束Erik Meijer和Microsoft的Peter Drayton:

静态类型的倡导者认为,静态类型的优点包括:较早检测到编程错误(例如,防止向布尔值添加整数),以类型签名的形式更好的文档(例如,在解析名称时引入参数的数量和类型),更多编译器优化的机会(例如,当静态已知接收器的确切类型时,通过直接调用替换虚拟调用),提高的运行时效率(例如,不是所有值都需要动态类型)以及更好的设计时间开发者体验接收者的类型,IDE可以显示所有适用成员的下拉菜单)。 静态打字狂热者试图让我们相信“精心打造的程序不会出错”。 虽然这听起来令人印象深刻,但这是一个相当空洞的声明。 静态类型检查是程序运行时行为的编译时抽象,因此它只是部分声音和不完整。 这意味着程序仍然可能因为没有被类型检查器跟踪的属性而出错,并且有些程序虽然不会出错,但不能进行类型检查。 由于诸如“幻影类型”[11]和“摇摆式类型”[10]等概念的出现,使得静态类型更少部分和更完整的冲动导致类型系统变得过于复杂和异乎寻常。 这就好比试图用一个连接在你腿上的球和链子跑马拉松,并高兴地大声呼喊,即使你在第一英里之后保释出来,你几乎可以做到这一点。

动态类型语言的倡导者认为,静态类型过于僵化,动态语言的柔和性使其非常适合具有变化或未知需求的原型系统,或者与其他不可预测地变化的系统(数据和应用程序集成)进行交互。 当然,动态类型语言对于处理真正动态的程序行为是必不可少的,例如方法拦截,动态加载,移动代码,运行时反射等等。在所有关于脚本的论文[16]中,John Ousterhout认为静态类型系统编程语言使得代码不像动态类型的脚本语言那样可重复使用,更加冗长,不是更安全并且表达力更低。 这种说法实际上是由许多动态类型化脚本语言的支持者提供的。 我们认为这是一种谬误,并且认为声明式编程的本质是取消赋值,这属于同一类别。 或者如约翰休斯所说[8],通过省略特征使语言更强大是一种逻辑上的不可能。 捍卫延迟所有类型检查到运行时间的事实是一件好事,因为在开发过程中尽可能早地发现错误,从而发挥鸵鸟策略。


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

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

出于这个原因,大多数类型系统提供了“转义”,这是覆盖静态检查器的方法。 对于大多数语言来说,这些都采用了铸造的形式,尽管有些(如C#和Haskell)具有标记为“不安全”的全部模式。

主观上,我喜欢静态打字。 正确实施(提示:不是Java),静态类型系统可以在错误消除生产系统之前清除错误方面提供巨大帮助。 动态类型的语言往往需要更多的单元测试,这在最好的时候是单调乏味的。 另外,静态类型语言可以具有某些在动态类型系统中不可能或不安全的特性(隐式转换可以放在心上)。 这全是一个需求和主观品味的问题。 我不会在Ruby中构建下一个Eclipse,而是尝试在Assembly中编写备份脚本或使用Java修补内核。

噢,那些说“x型打字机比打字机效率高10倍”的人只是在吹烟。 动态打字在许多情况下可能会“感觉”更快,但一旦实际尝试让您的应用程序运行时,它会失去基础。 同样,静态类型看起来可能是完美的安全网络,但是看一下Java中一些更复杂的泛型类型定义会导致大多数开发人员急匆匆地注视眼球。 即使有型系统和生产力,也没有银弹。

最后说明:在比较静态和动态类型时,不要担心性能问题。 V8和TraceMonkey等现代JIT正在危机中 - 接近静态语言表现。 而且,Java实际上编译成一种固有动态的中间语言这一事实​​应该暗示,在大多数情况下,动态类型并不是一些人所认为的巨大的性能杀手。


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

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

因此,静态类型不是从理论角度“防止错误”的方式,它们是语法的限制。 事实上,假设+,3和区间具有通常的集合理论定义,如果我们删除类型系统3 + [4,7]有一个相当明确的结果集合。 '运行时类型错误'理论上不存在,类型系统的实际使用是为了防止对人类的操作没有意义。 当然,操作仍然只是移位和操纵位。

这种情况的出现是,类型系统无法确定这种操作是否会发生,如果允许它运行的话。 如同,在所有可能的程序中准确地划分那些将会出现“类型错误”的程序,以及那些没有的类型。 它只能做两件事:

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

这可能看起来像我自相矛盾。 但是C或Java类型检查器所做的是拒绝程序为“不合法”,或者如果程序无法在2处成功,则称其为“类型错误”。它不能证明它们不会发生,这并不意味着它们不会发生,它只是意味着它不能证明它。 很可能是因为编译器无法证明一个没有类型错误的程序会被拒绝。 一个简单的例子是if(1) a = 3; else a = "string"; 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. , 而已。 从理论角度来看,没有动态类型语言这样的东西。 他们是无类型的

好吧,“动态输入”语言的明显优势是表现力,类型系统无非是表达力的限制。 一般来说,如果类型系统被忽略,那么具有类型系统的语言确实会对所有那些不允许的操作定义结果,但结果对人类来说是没有意义的。 许多语言在应用类型系统后会失去图灵的完整性。

明显的缺点是操作可能发生,这会产生对人类无意义的结果。 为了防止这种情况发生,动态类型语言通常会重新定义这些操作,而不是产生这种无意义的结果,他们将其重新定义为具有写出错误的副作用,并可能完全停止该程序。 这根本不是一个'错误',实际上,语言规范通常意味着这一点,这与从理论角度打印字符串的语言一样多。 因此,类型系统迫使程序员推断代码的流向,以确保不会发生这种情况。 或者的确,理由使得它确实发生在某些调试点也很方便,表明它不是一个“错误”,而是一个明确定义的语言属性。 实际上,大多数语言所具有的“动态输入”的单一残余部分正在防止零分割。 这就是动态类型,没有类型,没有更多的类型比零是一个不同于所有其他数字的类型。 人们称之为“类型”只是数据的另一个属性,如数组的长度或字符串的第一个字符。 许多动态类型语言也允许你写出"error: the first character of this string should be a 'z'"

另一件事是动态类型语言在运行时具有可用的类型,通常可以检查它并处理它并根据它进行判断。 当然,从理论上说,访问数组的第一个字符并查看它是什么并没有什么不同。 事实上,你可以创建自己的动态C,只使用long long这样的一个类型,并使用它的前8位来存储你的'类型'并相应地编写相应的函数来检查它并执行浮点或整数加法。 你有一种类型的静态类型语言,或者一种动态语言。

实际上,在所有节目中,静态类型语言通常用于编写商业软件,而动态类型语言则倾向于用于解决某些问题和自动执行某些任务。 用静态类型语言编写代码只需要很长时间,而且很麻烦,因为你不能做那些你知道会变好的事情,但是类型系统仍然能够保护你免受你自己犯的错误的困扰。 许多编码人员甚至都没有意识到他们这样做是因为它在系统中,但是当你使用静态语言进行编码时,你经常会考虑类型系统不会让你做不会出错的事情,因为它不能证明它不会出错。

正如我所指出的,“静态类型”一般意味着情况2,在证明无罪之前是有罪的。 但是一些语言,根本没有从类型理论中派生出他们的类型系统,使用规则1:无罪直到被证明有罪,这可能是理想的混合。 所以,也许Typed Racket适合你。

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

链接地址: http://www.djcxy.com/p/72303.html

上一篇: Dynamic type languages versus static type languages

下一篇: Reserved keywords count by programming language?