为什么解释的langs在编译时大部分都是鸭子型的?

sni*_*tko 44 compiler-construction programming-languages interpreted-language

我只是不知道,有什么技术原因吗?为弱键入的语言实现编译器是否更难?它是什么?

Nor*_*sey 199

问题背后的前提有点狡猾.解释性语言大多是鸭形的,这是不正确的.编译语言大多数都具有强类型,这是不正确的.该型系统是一种语言的属性. 编译与解释是的财产实施.

例子:

  • 编程语言计划是动态类型(又名鸭类型的),并且它有许多几十解释实现的,但也有一些细微的本地代码编译器,包括盗窃,开局和PLT计划(包括两者的解释和JIT编译器制作无缝过渡).

  • 编程语言Haskell是静态类型的; 两个最着名的实现是解释器HUGS和编译器GHC.在编译到本机代码(yhc)和解释(Helium)之间,还有其他几个可敬的实现.

  • 编程语言标准ML是静态类型的,它有许多本机代码编译器,其中最好和最主动维护的是MLton,但最有用的实现之一是解释器莫斯科ML

  • 编程语言Objective Caml是静态类型的.它配备了只有一个(在法国INRIA从)实现,但这实现包括译员和本机代码的编译器.

  • 编程语言Pascal是静态类型的,但它在20世纪70年代开始流行,因为在UCSD上构建了优秀的实现,它基于P代码解释器.在以后的几年中,可以使用精细的本机代码编译器,例如370系列计算机的IBM Pascal/VS编译器.

  • 编程语言C是静态类型的,今天几乎所有的实现都是编译的,但在20世纪80年代,我们幸运地使用Saber C的人正在使用解释器.

然而,你的问题背后有一些真相,所以你应该得到一个更周到的答案.事实上,动态类型语言似乎与解释的实现相关.为什么会这样?

  • 许多新语言由实现定义.构建解释器比构建编译器更容易.动态检查类型比静态检查更容易.如果您正在编写解释器,静态类型检查几乎没有性能优势.

  • 除非您正在创建或调整非常灵活的多态类型系统,否则静态类型系统可能会以程序员的方式进入.但是如果你正在编写一个解释器,一个原因可能是创建一个小的,轻量级的实现,不受程序员的影响.

  • 在一些解释型语言中,许多基本操作非常昂贵,以至于在运行时检查类型的额外开销无关紧要. 一个很好的例子就是PostScript:如果你要在帽子上跑掉并光栅化Bezier曲线,你就不会在这里或那里检查一个类型标签.

顺便说一句,请注意"强"和"弱"类型的术语,因为它们没有普遍认可的技术含义.相比之下,静态类型意味着在执行之前检查程序,并且程序在启动之前可能会被拒绝. 动态类型意味着执行期间检查的类型,并且类型不佳的操作可能导致程序在运行时停止或以其他方式发出错误信号.静态类型的主要原因是排除可能具有此类"动态类型错误"的程序.(这是编写解释器的人通常对静态类型不太感兴趣的另一个原因;在类型检查后立即执行,因此保证的区别和性质不那么明显.)

强类型通常意味着类型系统中没有漏洞,而弱类型意味着类型系统可以被破坏(使任何保证无效).这些术语经常被错误地用于表示静态和动态类型.为了看到差异,想想C:语言在编译时被类型检查(静态类型),但是存在很多漏洞; 你几乎可以将任何类型的值转换为相同大小的另一种类型 - 特别是,你可以自由地转换指针类型.Pascal是一种旨在强类型的语言,但着名的是有一个无法预料的漏洞:一个没有标签的变体记录.

强类型语言的实现通常会随着时间的推移而获得漏洞,通常是因为部分运行时系统可以用高级语言实现.例如,Objective Caml有一个函数调用Obj.magic,它具有简单地返回其参数的运行时效果,但在编译时它将任何类型的值转换为任何其他类型的值.我最喜欢的例子是Modula-3,其设计师称他们的类型转换结构LOOPHOLE.

综上所述:

  • 静态与动态是语言.

  • 编译与解释是实现.

  • 原则上,这两种选择可以是正交的,但是出于合理的技术原因,动态类型通常与解释相关.

  • @Norman,所以按照这个逻辑,每一种没有编译成字节码的解释性语言都是动态类型的,对吧? (2认同)

tsi*_*mon 9

你做早期绑定(强类型)的原因是性能.通过早期绑定,您可以在编译时找到方法的位置,以便在运行时它已经知道它的位置.

但是,对于后期绑定,您必须搜索一个看起来像客户端代码调用的方法的方法.当然,在程序中有许多方法调用,这就是动态语言"慢"的原因.

但是可以肯定的是,你可以创建一个静态编译的语言来进行后期绑定,这会否定静态编译的许多优点.


小智 5

因为编译语言在编译时需要考虑使用的内存量。

当您看到类似以下内容时:

int a
Run Code Online (Sandbox Code Playgroud)

在 C++ 中,编译器输出保留四个字节内存的代码,然后分配局部符号“a”指向该内存。如果您有像 javascript 这样的无类型脚本语言,解释器会在幕后分配所需的内存。你可以做:

var a = 10;  // a is probably a four byte int here
a = "hello world"; // now a is a 12 byte char array
Run Code Online (Sandbox Code Playgroud)

这两行之间发生了很多事情。解释器删除 a 处的内存,为字符分配新缓冲区,然后分配 a var 指向该新内存。在强类型语言中,没有解释器为您管理它,因此编译器必须编写考虑类型的指令。

int a = 10; // we now have four bytes on the stack.
a = "hello world"; // wtf? we cant push 12 bytes into a four byte variable! Throw an error!
Run Code Online (Sandbox Code Playgroud)

因此,编译器会停止编译该代码,因此 CPU 不会盲目地将 12 个字节写入 4 个字节的缓冲区并造成痛苦。

编译器编写额外指令来处理类型的额外开销会显着降低语言速度并消除像 C++ 这样的语言的好处。

:)

-纳尔逊

编辑以回应评论

我对 Python 了解不多,所以我不能说太多。但是松散类型会大大减慢运行时间。解释器 (VM) 调用的每条指令都必须进行评估,并在必要时将 var 强制转换为预期类型。如果你有:

mov a, 10
mov b, "34"
div a, b
Run Code Online (Sandbox Code Playgroud)

然后解释器必须确保 a 是一个变量和一个数字,然后它必须在处理指令之前将 b 强制转换为一个数字。为 VM 执行的每条指令添加开销,您的手就会一团糟:)


dkr*_*etz 5

这几乎是因为编写和使用解释型语言的人倾向于使用鸭子类型,而开发和使用编译型语言的人更喜欢强显式类型。(我认为这种情况的普遍原因应该是 90% 的错误预防和 10% 的性能。)对于今天编写的大多数程序,速度差异将是微不足道的。Microsoft Word 已经在 p 代码(未编译)上运行了 - 什么 - 15 年了?

我能想到的最好的例子。经典的 Visual Basic (VB6/VBA/etc.) 可以用 VB 编写相同的程序,并以相同的结果和相当的速度运行,无论是编译还是解释。此外,您可以选择是否进行类型声明(实际上是变量声明)。大多数人更喜欢类型声明,通常是为了防止错误。我从来没有听说过或在任何地方读到过使用类型声明来提高速度。这至少可以追溯到硬件速度和容量的两个数量级。

谷歌最近受到了很多关注,因为他们在 JavaScript 的 JIT 编译器上的工作 - 这不需要对语言进行更改,或者需要程序员的任何额外考虑。在这种情况下,唯一的好处就是速度。


Ste*_*owe 2

我猜测具有动态(鸭子)类型的语言采用惰性求值,这受到惰性程序员的青睐,而惰性程序员不喜欢编写编译器;-)

  • 呵呵,这是一个很好的文字游戏,但事实真相是什么呢?大多数具有动态(鸭子)类型的语言*不*执行惰性求值(默认情况下),而惰性求值在 Haskell 中最为著名,它具有静态类型系统和出色的编译器。:) (3认同)