与静态类型语言相比,动态类型语言的优点和局限是什么?
另请参阅:对动态语言的热爱(更具争议性的话题......)
解释器推断类型和类型转换的能力使开发时间更快,但它也可能引发运行时故障,而这些故障是您无法在编译时捕获它们的静态类型语言中获得的.但是这些天(以及很长一段时间以来)在社区中热烈讨论哪一个更好(或者即使总是如此).
关于这个问题的一个很好的解决方案是静态打字,如果需要,动态打字需要时: Erik Meijer和Peter Drayton在Microsoft 编程语言之间的冷战结束:
静态类型的倡导者认为静态类型的优点包括早期检测编程错误(例如,防止向布尔添加整数),更好的文档形式类型签名(例如,在解析名称时合并参数的数量和类型),更多编译器优化的机会(例如,当静态地知道接收器的确切类型时通过直接调用替换虚拟调用),提高运行时效率(例如,并非所有值都需要携带动态类型),以及更好的设计时开发人员体验(例如,知道接收器的类型,IDE可以显示所有适用成员的下拉菜单.静态打字狂热者试图让我们相信"打字好的程序不会出错".虽然这听起来令人印象深刻 这是一个相当空洞的陈述.静态类型检查是程序运行时行为的编译时抽象,因此它必然只是部分完整和不完整.这意味着程序仍然可能出错,因为类型检查器没有跟踪属性,并且有些程序虽然不会出错但却无法进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.静态类型检查是程序运行时行为的编译时抽象,因此它必然只是部分完整和不完整.这意味着程序仍然可能出错,因为类型检查器没有跟踪属性,并且有些程序虽然不会出错但却无法进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.静态类型检查是程序运行时行为的编译时抽象,因此它必然只是部分完整和不完整.这意味着程序仍然可能出错,因为类型检查器没有跟踪属性,并且有些程序虽然不会出错但却无法进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.因此,它必然只是部分健全和不完整.这意味着程序仍然可能出错,因为类型检查器没有跟踪属性,并且有些程序虽然不会出错但却无法进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.因此,它必然只是部分健全和不完整.这意味着程序仍然可能出错,因为类型检查器没有跟踪属性,并且有些程序虽然不会出错但却无法进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.并且有些程序虽然不会出错但是不能进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.并且有些程序虽然不会出错但是不能进行类型检查.使静态类型不那么局部和更完整的冲动导致类型系统变得过于复杂和异国情调,如"幻影类型"[11]和"摇摆类型"[10]等概念所证明的那样.这就像试图跑马拉松,球和链绑在你的腿上,胜利地喊着,即使你在第一英里后拯救出来,你几乎已经成功了.
动态类型语言的倡导者认为静态类型过于僵化,动态语言的柔和性使它们非常适合具有变化或未知需求的原型系统,或者与其他不可预测变化的系统(数据和应用程序集成)交互.当然,动态类型语言对于处理真正动态的程序行为是不可或缺的,例如方法拦截,动态加载,移动代码,运行时反射等.在所有关于脚本的论文的母亲[16]中,John Ousterhout认为静态类型系统与动态类型的脚本语言相比,编程语言使代码不再可重用,更冗长,更安全,更不具表现力.许多动态类型脚本语言的支持者从字面上看这个论点.我们认为这是一个谬论,并且与声明声明性编程的本质是消除任务有关.或者正如John Hughes所说[8],通过省略特征使语言更强大是不可能的.捍卫将所有类型检查延迟到运行时这一事实是一件好事,正在玩鸵鸟战术,因为错误应尽可能早地在开发过程中被捕获.通过省略特征使语言更强大是不可能的.捍卫将所有类型检查延迟到运行时这一事实是一件好事,正在玩鸵鸟战术,因为错误应尽可能早地在开发过程中被捕获.通过省略特征使语言更强大是不可能的.捍卫将所有类型检查延迟到运行时这一事实是一件好事,正在玩鸵鸟战术,因为错误应尽可能早地在开发过程中被捕获.
静态类型系统试图静态地消除某些错误,在不运行程序的情况下检查程序并试图在某些方面证明稳健性.某些类型系统能够捕获比其他系统更多的错误.例如,如果使用得当,C#可以消除空指针异常,而Java没有这种能力.Twelf有一个类型系统,它实际上保证了证据终止,"解决" 停止问题.
但是,没有类型系统是完美的.为了消除特定类别的错误,他们还必须拒绝某些违反规则的完全有效的程序.这就是为什么Twelf没有真正解决停止问题的原因,它只是通过抛出大量完全有效的证据来避免它,这些证据碰巧以奇怪的方式终止.同样,Java的类型系统PersistentVector
由于使用异构数组而拒绝Clojure的实现.它在运行时工作,但类型系统无法验证它.
因此,大多数类型系统提供"转义",即覆盖静态检查器的方法.对于大多数语言,这些采用铸造的形式,但有些(如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类型的检查器做的是它拒绝一个程序为'ungrammatical',或者如果它在2处不能成功则称它为'type error' .它无法证明它们不会发生,这并不意味着它们不会发生,它只是意味着它无法证明它.一个不会出现类型错误的程序很可能会因为编译器无法证明而被拒绝.一个简单的例子就是if(1) a = 3; else a = "string";
当然,因为它始终是真的,所以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位来存储你的'type'并相应地写函数来检查它并执行浮点或整数加法.您有一种静态类型语言,只有一种类型或动态语言.
在实践中,所有显示,静态类型语言通常用于编写商业软件的上下文中,而动态类型语言倾向于在解决某些问题和自动化某些任务的上下文中使用.用静态类型语言编写代码只需要很长时间并且很麻烦,因为你不能做你知道的事情会好转,但类型系统仍会保护你免受你自己的错误.许多程序员甚至没有意识到他们这样做是因为它在他们的系统中,但是当你用静态语言编写代码时,你经常会解决类型系统不会让你做出不会出错的事情的事实,因为它无法证明它不会出错.
正如我所指出的,"静态打字"通常意味着案例2,在被证明无罪之前有罪.但是有些语言根本没有从类型理论中推导出它们的类型系统使用规则1:无辜者直到被证明有罪,这可能是理想的混合体.所以,也许Typed Racket适合你.
另外,对于一个更荒谬和极端的例子,我目前正在实现一种语言,其中'types'真正是数组的第一个字符,它们是数据,'type','type'的数据,它本身就是类型和基准,唯一将其本身作为类型的基准.类型不是静态的有限的或有界的,但可以基于运行时信息生成新类型.
也许动态打字的最大"好处"是较浅的学习曲线.没有要学习的类型系统,也没有类型约束等极端情况的非平凡语法.这使得动态类型可以被更多人访问,并且对于许多人来说是可行的,对于那些复杂的静态类型系统是不可及的.因此,动态类型已经在教育的背景下(例如麻省理工学院的Scheme/Python)和非程序员的领域特定语言(例如Mathematica)流行起来.动态语言也在他们很少或没有竞争的利基(例如Javascript)中流行起来.
最简洁的动态类型语言(例如Perl,APL,J,K,Mathematica)是特定于域的,并且可以比它们为其设计的壁龛中最简洁的通用静态类型语言(例如OCaml)更简洁. .
动态类型的主要缺点是:
运行时类型错误.
实现相同水平的正确性可能非常困难甚至几乎不可能,并且需要大量的测试.
没有编译器验证的文档.
性能不佳(通常在运行时但有时在编译时,例如斯大林方案)和由于依赖于复杂的优化而导致的不可预测的性能.
就个人而言,我是在动态语言中长大的,但除非没有其他可行的选择,否则不会用40英尺杆作为专业人士.
从Artima的打字:强与弱,静态与动态文章:
强类型可防止不匹配类型之间的混合操作.要混合类型,您必须使用显式转换
弱类型意味着您可以在没有显式转换的情况下混合类型
在Pascal Costanza的论文" 动态与静态打字 - 基于模式的分析"(PDF)中,他声称在某些情况下,静态类型比动态类型更容易出错.一些静态类型的语言强迫您手动模拟动态类型,以便做"正确的事".它在Lambda the Ultimate上进行了讨论.