与静态类型语言相比,动态类型语言的优点和局限性是什么?
另请参阅:对动态语言的热爱(更具争议性的主题...)
与静态类型语言相比,动态类型语言的优点和局限性是什么?
另请参阅:对动态语言的热爱(更具争议性的主题...)
Answers:
解释器推断类型和类型转换的能力使开发时间更快,但是它也可能引发运行时失败,而您在静态类型语言中就无法遇到这种情况,而您只能在编译时捕获它们。但是,这些天来(以及很长一段时间以来),社区中一直在热烈讨论哪个更好(甚至永远都是这样)。
这个问题的一个很好的选择是来自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],通过省略功能使语言更强大是一种逻辑上的不可能。捍卫将所有类型检查延迟到运行时是一件好事的事实,这是一种愚蠢的策略,其事实是应在开发过程中尽早发现错误。通过省略功能来使语言更强大是逻辑上不可能的。捍卫将所有类型检查延迟到运行时是一件好事的事实,这是一种愚蠢的策略,其事实是应在开发过程中尽早发现错误。通过省略功能来使语言更强大是逻辑上不可能的。捍卫将所有类型检查延迟到运行时是一件好事的事实,这是一种愚蠢的策略,其事实是应在开发过程中尽早发现错误。
静态类型系统寻求静态消除某些错误,在不运行程序的情况下检查程序,并尝试在某些方面证明其可靠性。某些类型的系统比其他类型的系统能够捕获更多的错误。例如,正确使用C#可以消除空指针异常,而Java没有这种能力。Twelf具有一个类型系统,实际上可以保证证明将终止,从而“解决” 暂停问题。
但是,没有类型系统是完美的。为了消除特定类别的错误,它们还必须拒绝某些违反规则的完全有效的程序。这就是为什么Twelf不能真正解决暂停问题的原因,它只是抛出大量完全有效的证明而以奇怪的方式终止,从而避免了该问题。同样,Java的类型系统PersistentVector
由于使用异类数组而拒绝Clojure的实现。它在运行时有效,但是类型系统无法验证它。
因此,大多数类型系统都提供“转义符”,即覆盖静态检查器的方法。对于大多数语言,它们采用强制转换的形式,尽管某些语言(例如C#和Haskell)具有标记为“不安全”的整个模式。
从主观上讲,我喜欢静态键入。正确地实现(提示:不是 Java),静态类型系统可以在错误使生产系统崩溃之前清除错误。动态类型的语言往往需要更多的单元测试,这在最佳情况下是很乏味的。同样,静态类型的语言可能具有某些特性,这些特性在动态类型系统中是不可能的或不安全的(隐式转换浮现在脑海)。这都是要求和主观品味的问题。与尝试在Assembly中编写备份脚本或使用Java修补内核一样,我将不会再在Ruby中构建下一个Eclipse。
哦,有人说“ x打字的效率是y打字的10倍”,只是在吹烟。在许多情况下,动态键入可能“感觉”得更快,但是一旦您实际尝试使精美的应用程序运行,它就会失去基础。同样,静态类型看似似乎是完美的安全网,但一看Java中一些更复杂的泛型类型定义,大多数开发人员就会争先恐后。即使具有类型系统和生产率,也没有灵丹妙药。
最后说明:将静态类型与动态类型进行比较时,不必担心性能。V8和TraceMonkey之类的现代JIT危险地接近静态语言性能。而且,Java实际上可以编译为固有的动态中间语言这一事实应该表明,在大多数情况下,动态类型并不是某些人认为的巨大性能杀手。
dadd
因为编译器选择指令(例如因为他事先知道操作数为double
s),所以不需要覆盖这些检查的其他运行时检查。
好吧,这两者都是非常非常非常非常容易被误解的东西,也是两个完全不同的东西。不是互相排斥的。
静态类型是语言语法的限制。严格来说,静态类型的语言可以说不是上下文无关的。一个简单的事实是,在上下文无关的语法中明智地表达一种语言变得不方便,而上下文无关的语法不能将其所有数据简单地视为位向量。静态类型系统是语言语法的一部分(如果有的话),它们对上下文的限制要远远超过上下文无关的语法,因此语法检查实际上是在源代码的两次遍历中进行的。静态类型对应于类型理论的数学概念,数学中的类型理论仅限制了某些表达式的合法性。就像,我不能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适合您。
同样,对于一个更荒谬和极端的例子,我目前正在实现一种语言,其中“类型”确实是数组的第一个字符,它们是数据,数据本身就是“类型”,“类型”类型和基准,唯一具有自身作为类型的基准。类型不是有限的或静态的,但是可以根据运行时信息生成新的类型。
动态打字的最大的“好处”也许就是学习曲线较浅。没有学习类型系统,也没有针对特殊情况(例如类型约束)的非平凡语法。这使得动态类型可供更多人使用,并且对于许多无法使用复杂的静态类型系统的人来说也是可行的。因此,动态类型已经在教育(例如MIT的Scheme / Python)和非程序员的领域特定语言(例如Mathematica)的环境中流行起来。动态语言也已经占据了竞争很少或没有竞争的壁ni(例如Javascript)。
最简洁的动态类型化语言(例如Perl,APL,J,K, Mathematica)是特定于领域的,并且在其专门设计的领域中比最简洁的通用静态类型化语言(例如OCaml)更为简洁。 。
动态类型化的主要缺点是:
运行时类型错误。
要达到相同的正确性可能非常困难,甚至几乎是不可能的,并且需要进行大量测试。
没有经过编译器验证的文档。
由于依赖复杂的优化,性能较差(通常在运行时,但有时在编译时,例如,斯大林方案),并且性能不可预测。
我个人是在动态语言下长大的,但除非有其他可行的选择,否则我不会以40英尺的杆位触动他们。
摘自Artima的打字:强vs.弱,静态vs.动态文章:
强类型可防止不匹配类型之间的混合操作。为了混合类型,必须使用显式转换
弱类型意味着您无需显式转换即可混合类型
在Pascal Costanza的论文中, 动态与静态打字-基于模式的分析(PDF)》中,他声称在某些情况下,静态打字比动态打字更容易出错。一些静态类型的语言会强迫您手动模拟动态类型,以执行“正确的事情”。在 Lambda Ultimate上进行了讨论。
静态打字: Java和Scala等语言是静态类型的。
在代码中使用变量之前,必须先定义和初始化变量。
对于前。int x; x = 10;
System.out.println(x);
动态打字: Perl是一种动态类型的语言。
在代码中使用变量之前,无需对其进行初始化。
y = 10;在代码的后面部分使用此变量