关于编译器构造:解释器和动态类型语言

Interpreters and Dynamically Typed Languages

为什么具有动态类型语言的程序通常被解释而不是编译?


简而言之:它们像豌豆和胡萝卜一样在一起。

编译、解释和语言类型是基本上独立的关注点,因为您可以拥有所有可能的排列。另一方面,为语言设计选择编译和不选择动态类型的"原因"通常是相同的:性能。选择动态类型和解释的"原因"也有一定的关联。

这不是一条硬性的规定。你可以把它们混在一起。例如,您可以编译Perl和Lisp并解释C。


你观察到一个非因果关系:

  • 动态类型和解释相互关联,因为两者都易于实现。
  • 静态类型和编译是相互关联的,因为两者都有利于预期的良好性能。

为了提高性能(因为性能通常非常差),编译器通常被改装成动态类型语言。例如,下面介绍了在编写第一个编译器之前,对一些主要的动态类型语言进行解释的时间:lisp(1958-1962)、mathematica(1988-2004)、lua(1993-2004)、python(1991-2002)和javascript(1995-2009)。相比之下,像ocaml(1996)和f(2001)这样的语言首先作为编译器发布。


正如其他人所指出的,语言既不被编译也不被解释。它们只是需要翻译的规则,大多数都有解释和编译的实现。即便如此,当许多"解释器"在各处抖动时,也很难讨论解释与编译,而且如果源文件发生更改,一些"编译器"也乐于按需编译。

也许最好将实现分类为完全预编译或按需编译。如果我们使用这些类别,那么破坏完整预编译的一件事就是eval函数。这可能比动态类型对实现的影响更大。如果您有一个eval函数,则需要支持按需编译。


常用的lisp代码主要是编译的。通用的lisp编程语言已经在支持编译的ANSI标准中描述过。ANSI标准描述了编译代码的函数,描述了优化的各个方面,描述了编译过程的各个方面等等。

常见Lisp的解释程序存在,但使用频率较低。

常见的Lisp实现通常可以自由地混合不同的执行模式。几乎所有的软件都有一个编译器。少数只有一个编译器。

几乎所有实现中的编译都有一个增量模式,因此可以交互使用。所有人都可以编译文件。有些具有"块编译"模式来编译文件组。


根据动态类型语言的定义…

A programming language is said to be dynamically typed when the majority of its type checking is performed at run-time as opposed to at compile-time. In dynamic typing, values have types but variables do not; that is, a variable can refer to a value of any type.

也就是说,由于程序在运行时可能发生变化,所以很难知道要将它编译成什么。维基百科的另一个摘录…

Dynamic typing allows constructs that
some static type checking would reject
as illegal. For example, eval
functions, which execute arbitrary
data as code, become possible.
Furthermore, dynamic typing better
accommodates transitional code and
prototyping, such as allowing a
placeholder data structure (mock
object) to be transparently used in
place of a full-fledged data structure
(usually for the purposes of
experimentation and testing).

我觉得这个答案仍然不完整,但我希望它能为你指明正确的方向。也许这里的其他人可以在此基础上进行扩展。


检查类型一次实际上就是"编译器"(~类型检查器)的原因。

当类型为"动态"时,不能"编译"(检查类型一次)。