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

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

另请参阅: 对动态语言的热爱是怎么回事(一个更具争议性的线程…)

129217 次浏览

解释器推断类型和类型转换的能力使开发时间更快,但它也可能引发运行时失败,这是在静态类型语言中无法得到的,因为在编译时就可以捕捉到。但是哪一个更好(或者即使这是真的)最近在社区里被热议(而且已经持续了很长一段时间)。

微软的Erik Meijer和Peter Drayton在可能时使用静态类型,需要时使用动态类型:编程语言之间冷战的结束中对这个问题有一个很好的解释:

静态类型的提倡者认为 静态类型的优点 包括及早发现 编程错误(例如防止 将整数添加到布尔值), 更好的文档形式 类型签名(例如合并 参数的数量和类型 解析名字),更多的机会 对于编译器优化(例如: 用直接调用替换虚拟调用 类的确切类型时调用 接收器是静态已知的), 提高运行时效率(例如,不能 所有值都需要动态承载 类型),以及更好的设计时间 开发人员经验(例如了解 类型的接收器,IDE可以 呈现所有的下拉菜单 适用的成员)。静态类型 狂热分子试图让我们相信这一点 “类型良好的程序不会出错”。 虽然这听起来 令人印象深刻的是,它相当空洞 声明。静态类型检查是一种 的编译时抽象 程序的运行时行为,以及 因此,它必然只是部分的 健全的,不完整的这意味着 程序仍然可能出错,因为 属性未跟踪的 类型检查器,还有 程序,而他们不能去 错误不能被类型检查。的 减少静态类型的冲动 部分原因和更完整的原因类型 系统变得过于复杂 由概念所见证的异域风情 如“幻影类型”[11]和 “摇摆类型”[10]。这就像 拿着球跑马拉松 腿上绑着链子 得意洋洋地喊着你差点 尽管你逃出来了

动态类型的倡导者 语言认为静态类型是 太过僵硬,那份柔软 动态语言使它们 非常适合于原型系统 对于不断变化的或未知的需求, 或者与其他系统相互作用 这种变化是不可预测的(数据和 应用程序集成)。当然, 动态类型语言包括 真正的处理是不可缺少的 动态程序行为,如 方法截取,动态加载, 移动代码、运行时反射等。 在所有的文件中 John Ousterhout认为,脚本[16] 即静态类型系统 编程语言使代码更少 可重复使用,更冗长,不是更安全, 表现力不如动态的 类型化脚本语言。这 争论被许多人逐字逐句地重复着 动态类型的支持者 脚本语言。我们认为 这是一个谬论,属于 这和争论 声明式编程的本质是 取消任务。或者叫约翰 休斯说[8],这是合乎逻辑的 不可能创造更多的语言 通过省略功能而强大。 为拖延所有的事实辩护 运行时的类型检查是一种很好的方法 事情,就是玩鸵鸟战术 错误应该被捕捉的事实 在开发过程的早期 可能的。< / p >

来自Artima的打字:强vs弱,静态vs动态文章:

强类型可以防止在不匹配的类型之间混合操作。为了混合类型,必须使用显式转换

弱类型意味着可以在没有显式转换的情况下混合类型

在Pascal Costanza的论文动态vs静态类型-基于模式的分析 (PDF)中,他声称在某些情况下,静态类型比动态类型更容易出错。一些静态类型的语言强迫你手动模拟动态类型,以便做“正确的事情”。它在终极中讨论过。

关于静态语言和动态语言有很多不同的东西。对我来说,主要的区别是在动态语言中,变量没有固定的类型;相反,类型被绑定到值。因此,要执行的确切代码直到运行时才确定。

在早期或naïve实现中,这是一个巨大的性能拖累,但现代jit已经非常接近优化静态编译器的最佳性能。(在一些边缘情况下,甚至比这更好)。

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

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

因此,大多数类型系统都提供了“转义”,即覆盖静态检查器的方法。对于大多数语言来说,它们采取强制转换的形式,尽管有些语言(如c#和Haskell)有完整的模式被标记为“不安全”。

主观上,我喜欢静态类型。如果实现得当(提示: Java),静态类型系统可以在错误导致生产系统崩溃之前清除错误。动态类型语言往往需要更多的单元测试,即使在最好的情况下,这也是乏味的。此外,静态类型语言可能具有在动态类型系统中不可能或不安全的某些特性(隐式转换首先映入脑海)。这完全是需求和主观品味的问题。我不会用Ruby构建下一个Eclipse,就像我不会尝试用Assembly编写备份脚本或使用Java为内核打补丁一样。

哦,那些说“x输入的效率是y输入的10倍”的人只是在吹牛。在许多情况下,动态类型可能“感觉”更快,但一旦你真正尝试让你的花哨应用程序运行,它就失去了优势。同样地,静态类型可能看起来是完美的安全网,但只要看一下Java中一些更复杂的泛型类型定义,大多数开发人员就会匆忙寻找令人眼花缭乱的东西。即使有类型系统和生产力,也没有什么灵丹妙药。

最后注意:在比较静态类型和动态类型时,不要担心性能。像V8和TraceMonkey这样的现代jit正在危险地接近静态语言性能。此外,Java实际上被编译为一种固有的动态中间语言,这一事实应该表明,在大多数情况下,动态类型并不是一些人认为的巨大性能杀手。

关键是要有合适的工具。这两种方法都不是100%的好方法。这两种制度都是人为创造的,都有缺陷。抱歉,我们做的东西太烂了。

我喜欢动态类型,因为它让我摆脱了我的方式,但是是的,运行时错误会悄悄出现,我没有计划到。 静态类型可能会修复前面提到的错误,但会让新手(在类型化语言中)程序员疯狂地尝试在常量字符和字符串之间进行强制转换

这取决于上下文。动态类型系统和强类型系统都有很多优点。我认为动态类型语言的流动更快。动态语言不受类属性的限制,编译器也不考虑代码中发生的事情。你有一些自由。此外,动态语言通常更有表现力,代码更少,这是很好的。尽管如此,它更容易出错,这也是值得怀疑的,并且更多地依赖于单元测试覆盖。这是一个简单的动态模型,但维护可能成为噩梦。

相对于静态类型的系统,主要的好处是IDE支持,当然还有静态代码分析器。 在每次代码更改之后,您都会对代码更加自信。用这样的工具来维护蛋糕是和平的。< / p >

也许动态类型最大的“好处”是更短的学习曲线。这里没有需要学习的类型系统,也没有用于极端情况(如类型约束)的重要语法。这使得更多的人可以使用动态类型,对于那些复杂的静态类型系统遥不可及的人来说也是可行的。因此,动态类型在教育环境(例如麻省理工学院的Scheme/Python)和面向非程序员的领域特定语言(例如Mathematica)中流行起来。动态语言还在竞争对手很少或没有竞争对手的小众领域(如Javascript)流行起来。

最简洁的动态类型语言(例如Perl, APL, J, K, Mathematica)是特定于领域的,并且在它们设计的利基领域中比最简洁的通用静态类型语言(例如OCaml)要简洁得多。

动态类型的主要缺点是:

  • 运行时类型错误。

  • 要达到相同水平的正确性是非常困难的,甚至实际上是不可能的,并且需要大量的测试。

  • 没有经过编译器验证的文档。

  • 糟糕的性能(通常在运行时,但有时在编译时,例如斯大林方案)和由于依赖复杂的优化而不可预测的性能。

就我个人而言,我是伴随着动态语言长大的,但作为一名专业人士,除非没有其他可行的选择,否则不会用40英尺的杆子接触它们。

这两者都被误解得非常非常非常严重,而且是完全不同的两件事。它们不是相互排斥的

静态类型是语言语法的一个限制。严格地说,静态类型语言不是与上下文无关的。一个简单的事实是,用上下文无关的语法理智地表达一种语言变得很不方便,因为它不把所有的数据都简单地当作位向量来对待。静态类型系统是语言语法的一部分(如果有的话),它们只是比上下文无关的语法更能限制它,因此语法检查实际上在两次遍历源代码时发生。静态类型对应于类型论的数学概念,类型论在数学中只是限制了某些表达式的合法性。比如,我不能在数学中说3 + [4,7],这是因为它的类型理论。

因此,静态类型不是一种“防止错误”的方法,从理论的角度来看,它们是语法的限制。事实上,假设+、3和间隔具有通常的集合理论定义,如果我们删除类型系统__abc0,则有一个定义很好的结果,即一个集合。'运行时类型错误'理论上不存在,类型系统的实际用途是防止对人类来说没有意义的操作。当然,操作仍然只是位的移位和操作。

这样做的问题在于,类型系统无法决定是否将发生这样的操作(如果允许它运行)。也就是说,准确地将所有可能的程序集划分为那些将会有“类型错误”的程序和那些没有“类型错误”的程序。它只能做两件事:

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

这看起来好像我在自相矛盾。但是C或Java类型检查器所做的是拒绝一个“不合语法”的程序,或者如果它不能成功于2,它就称之为“类型错误”。它不能证明它们不会发生,这并不意味着它们不会发生,它只是意味着它不能证明它。一个没有类型错误的程序很可能会被拒绝,因为它不能被编译器证明。一个简单的例子是if(1) a = 3; else a = "string";,当然,由于它总是为真,else分支永远不会在程序中执行,也不会发生类型错误。但它不能以一般的方式证明这些情况,所以被驳回。这是很多静态类型语言的主要弱点,在保护你不受自己的伤害时,你也必须在你不需要它的情况下受到保护。

但是,与流行的观点相反,还有一些静态类型语言是根据原则1工作的。他们只是拒绝所有他们能证明会导致类型错误的程序,并通过所有他们不能证明会导致类型错误的程序。所以有可能它们允许程序中有类型错误,一个很好的例子是类型化球拍,它是动态和静态类型的混合。有些人会说,在这个系统中,你得到了两全其美的东西。

静态类型的另一个优点是类型在编译时已知,因此编译器可以使用它。如果我们在Java中执行"string" + "string"3 + 3,这两个+令牌在文本中最后表示完全不同的操作和数据,编译器只知道从类型中选择哪一个。

现在,我要在这里做一个非常有争议的陈述,但请容忍我:“动态类型”并不存在

听起来很有争议,但这是真的,动态类型语言是从理论的角度无类型。它们只是只有一种类型的静态类型语言。或者简单地说,它们确实是在实践中由上下文无关的语法生成的语言。

为什么他们没有类型?因为每个操作都在每个操作上被定义和允许,那么什么是“运行时类型错误”呢?它来自一个纯粹的理论例子副作用。如果执行打印字符串的print("string")是一个操作,那么length(3)也是一个操作,前者的副作用是将string写入标准输出,后者只是error: function 'length' expects array as argument.,仅此而已。从理论的角度来看,动态类型语言是不存在的。他们是无类型

好吧,“动态类型”语言的明显优势是表达能力,类型系统只不过是表达能力的限制。一般来说,带有类型系统的语言确实会为所有不允许的操作定义一个结果,如果忽略类型系统,结果对人类来说就没有意义了。许多语言在应用类型系统后失去了图灵完整性。

明显的缺点是,操作可能会产生对人类来说毫无意义的结果。为了防止这种情况,动态类型语言通常会重新定义这些操作,而不是产生那些无意义的结果,而是将其重新定义为具有写出错误的副作用,并可能完全停止程序。这根本不是一个“错误”,事实上,语言规范通常暗示这一点,从理论角度来看,这是语言的行为,就像打印字符串一样。因此,类型系统迫使程序员对代码流进行推理,以确保这种情况不会发生。或者实际上,通过推理使发生在某些调试点上也很方便,这表明它根本不是一个“错误”,而是语言的一个定义良好的属性。实际上,大多数语言所保留的“动态类型”的唯一残余就是防止除零。这就是动态类型,没有类型,没有比0和其他数字不同的类型。人们所说的“类型”只是数据的另一个属性,就像数组的长度,或者字符串的第一个字符。许多动态类型语言也允许你写出"error: the first character of this string should be a 'z'"这样的东西。

另一件事是,动态类型语言在运行时具有可用的类型,通常可以检查它,处理它,并从中做出决定。当然,从理论上讲,这与访问数组的第一个char并查看它是什么没有什么不同。事实上,你可以创建自己的动态C语言,只使用一种类型,比如long long int,并使用它的前8位来存储你的“类型”,并相应地编写函数来检查它并执行浮点数或整数加法。你有一种静态类型语言和一种动态语言。

实践表明,静态类型语言通常用于编写商业软件,而动态类型语言倾向于用于解决某些问题和自动化某些任务。用静态类型语言编写代码既费时又麻烦,因为你不能做你知道结果会好的事情,但类型系统仍然保护你不犯你不会犯的错误。许多编码员甚至没有意识到他们这样做,因为这是在他们的系统中,但当你用静态语言编码时,你经常要面对这样一个事实,即类型系统不会让你做不会出错的事情,因为它不能证明它不会出错。

正如我所指出的,“静态类型”通常意味着情况2,在被证明无罪之前是有罪的。但是,有些语言根本不是从类型理论中派生出类型系统的,它们使用规则1:在被证明有罪之前是无辜的,这可能是理想的混合。所以,也许打字球拍适合你。

还有一个更荒谬和极端的例子,我目前正在实现一种语言,其中'类型'是数组的第一个字符,它们是数据,'类型'的数据,'类型'本身就是一个类型和数据,唯一的数据本身就是一个类型。类型不是静态的有限或有界的,但是可以根据运行时信息生成新类型。

< >强静态类型: Java、Scala等语言为静态类型

在代码中使用变量之前,必须对变量进行定义和初始化。

< p >为例。 int x; X = 10;

System.out.println (x);

< >强动态类型: Perl是一种动态类型语言

变量在代码中使用之前不需要初始化。

y = 10;在后面的代码中使用这个变量