为什么解释的langs大多是ducktyped而编译有强大的打字?
我只是不知道,这是否有任何技术原因? 对于input较弱的语言,实现编译器更难吗? 它是什么?
这个问题背后的前提是有点狡猾 。 解释型语言大多是鸭型的,这是不正确的。 编译语言大多数都有很强的input,这是不正确的。 types系统是一种语言的属性 。 编译与解释是实现的一个属性。
例子:
-
编程语言Scheme是dynamictypes的(又名duck-typed),它有许多解释实现,还有一些很好的本地代码编译器,包括Larceny,Gambit和PLT Scheme(它包括一个解释器和一个JIT编译器无缝转场)。
-
编程语言Haskell是静态types的; 最着名的两个实现是解释器HUGS和编译器GHC 。 在编译为本地代码(yhc)和解释(氦)之间,还有几个其他的荣誉实现可以平均分配。
-
编程语言Standard ML是静态types的,它有许多本地代码编译器,其中最好和最积极的维护之一是MLton ,但最有用的实现之一是解释器莫斯科ML
-
编程语言Objective Caml是静态types的。 它只有一个实现(来自法国的INRIA),但是这个实现包括一个解释器和一个本地代码编译器。
-
编程语言Pascal是静态types的,但由于在UCSD基础上build立了一个基于P代码解释器的优秀实现,它在20世纪70年代变得stream行起来。 在以后的几年中,精细的本地代码编译器变得可用,例如用于370系列计算机的IBM Pascal / VS编译器。
-
编程语言C是静态types的,今天几乎所有的实现都被编译了,但是在八十年代,我们幸运地使用Sabre C的人正在使用一个解释器。
不过你的问题背后有一些事实 ,所以你应该有一个更深思熟虑的答案。 事实是, dynamictypes语言似乎与解释的实现相关联 。 为什么会这样?
-
许多新语言是由一个实现定义的。 构build解释器比构build编译器更容易。 dynamic检查types比静态检查更容易。 如果你正在编写一个解释器,静态types检查几乎没有性能好处。
-
除非你正在创build或调整一个非常灵活的多态types系统,否则静态types系统可能会以程序员的方式进入。 但是,如果你正在编写一个解释器,一个原因可能是创build一个小的,轻量级的实现,而不是程序员的方式。
-
在某些解释型语言中, 许多基本操作非常昂贵,以至于在运行时检查types的额外开销并不重要。 一个很好的例子就是PostScript:如果你打算在一顶帽子里跑一走贝塞尔曲线,你就不会在这里或那里检查一个types标签。
顺便提一句,请小心“强”和“弱”的打字 ,因为他们没有普遍认同的技术含义。 相比之下, 静态types意味着程序在被执行之前被检查 ,并且程序在开始之前可能被拒绝。 dynamictypes意味着在执行期间检查值的types,而types错误的操作可能会导致程序在运行时暂停或以其他方式发出错误信号 。 静态types的主要原因是排除可能有这种“dynamictypes错误”的程序。 (这是编写解释器的人通常对静态input不感兴趣的另一个原因;在执行types检查之后立即执行,所以担保的区别和性质不那么明显)。
强types一般意味着types系统没有漏洞 ,而弱types意味着types系统可以被颠覆(使任何保证无效)。 这些术语经常被错误地用来表示静态和dynamic的input。 为了看清楚它们之间的区别,可以想象C语言是在编译时进行types检查的(静态types),但是有很多漏洞; 你几乎可以将任何types的值转换为相同大小的另一种types—特别是可以自由地转换指针types。 帕斯卡尔是一种意欲强烈打字的语言,但着名的却有一个无法预料的漏洞:一个没有标签的变种logging。
强types语言的实现往往会随着时间的推移而产生漏洞,通常这样一来,运行时系统的一部分就可以用高级语言来实现。 例如,Objective Caml有一个名为Obj.magic
的函数,它具有简单地返回它的参数的运行时间效果,但是在编译时它将任何types的值转换成任何其他types的值。 我最喜欢的例子是Modula-3,他们的devise者称他们的types铸造构造LOOPHOLE
。
综上所述:
-
静态与dynamic是语言 。
-
编译vs解释是实现 。
-
原则上这两种select可以是正交的 ,但是由于技术上的原因, dynamic分类经常与解释相关 。
你做早期绑定(强打字)的原因是性能。 在早期绑定的情况下,您可以在编译时find该方法的位置,以便在运行时它已经知道它的位置。
但是,如果使用后期绑定,则必须search一种类似于客户端代码所调用方法的方法。 当然,在一个程序中调用许多方法,这就是dynamic语言“慢”的原因。
但是可以肯定的是,你可以创build一个静态编译的语言来进行后期绑定,这将否定静态编译的许多优点。
这很大程度上是因为编写和使用解释型语言的人倾向于使用ducktyping,而开发和使用编译型语言的人则更喜欢使用明确的键入。 (我认为这个意见的理由在防止错误方面占90%,在performance方面占10%)。对于今天编写的大多数程序来说,速度差异是微不足道的。 微软的Word已经运行在P代码(未编译) – 什么 – 现在15年了?
我可以想到最好的例子。 经典的Visual Basic(VB6 / VBA /等)在VB中可以编写相同的程序,并运行相同的结果和编译或解释的速度。 此外,你可以selecttypes声明(实际上是variables声明)或不。 大多数人喜欢types声明,通常用于防止错误。 我从来没有听说过或读过任何地方使用types声明的速度。 这在硬件速度和容量上至less可以达到两个数量级。
Google最近引起了很多关注,因为他们在JavaScript的JIT编译器上工作 – 这将不需要对语言进行任何更改,或者需要程序员的额外考虑。 在这种情况下,唯一的好处就是速度。
我猜dynamic(鸭)打字的语言使用懒惰的评估,这是懒惰的程序员青睐,懒惰的程序员不喜欢编写编译器;-)
因为编译语言在编译时需要占用大量的内存。
当你看到像这样的东西:
int a
在C ++中,编译器吐出保留四字节内存的代码,然后分配本地符号“a”指向该内存。 如果你有一个无types的JavaScript脚本语言,解释器在后台分配所需的内存。 你可以做:
var a = 10; // a is probably a four byte int here a = "hello world"; // now a is a 12 byte char array
这两条线之间发生了很多事情。 解释器删除a处的内存,为chars分配新的缓冲区,然后指定一个var指向新的内存。 在强types语言中,没有为您pipe理的解释器,因此编译器必须编写考虑types的指令。
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!
所以编译器会停止编译代码,所以CPU不会盲目地将12个字节写入4字节的缓冲区并造成不幸。
编译器编写额外指令来处理types的额外开销会显着减慢语言,并消除像C ++这样的语言的好处。
🙂
-nelson
编辑回应评论
我不太了解Python,所以我不能多说这些。 但松散的types会显着减慢运行时间。 解释器(VM)调用的每个指令都必须执行,并且必要时将var强制为预期的types。 如果你有:
mov a, 10 mov b, "34" div a, b
然后解释器必须确保a是一个variables和一个数字,那么在处理指令之前,必须将b强制为数字。 为虚拟机执行的每条指令添加这个开销,并且你的手上有一堆乱七八糟的东西:)
基本上有两个原因使用静态键入鸭打字:
- 静态错误检查。
- 性能
如果你有一个解释的语言,那么没有编译时间来进行静态错误检查。 有一个好处。 而且,如果你已经有了解释器的开销,那么这个语言已经不会被用于任何关键的性能,所以性能的说法就变得无关紧要了。 这就解释了为什么静态types的解释语言很less见。
换句话说,在静态types语言中鸭子打字可以在很大程度上被模拟,而不会完全放弃静态打字的好处。 这可以通过以下任何一个来完成:
- 模板。 在这种情况下,如果您实例化模板的types支持从模板中调用的所有方法,则代码将进行编译和工作。 否则,会产生编译时错误。 这就像编译时鸭式打字一样。
- reflection。 您尝试通过名称来调用方法,它可以工作或引发exception。
- 标记的工会。 这些基本上是包含一些内存空间和描述当前包含的types的字段的其他types的容器类。 这些用于像代数types的东西。 当一个方法被调用时,它可以工作或抛出,取决于当前包含的types是否支持它。
这就解释了为什么很less有dynamictypes的编译语言。
弱types的语言可以编译,例如,Perl5和大多数Lisp版本都是编译语言。 然而,编译的性能好处常常会丢失,因为语言运行时必须执行的大部分工作是确定dynamicvariables在特定时间具有的types。
以Perl中的下面的代码为例:
$x=1; $x="hello"; print $x;
编译器显然很难确定$ x在给定的时间点上的真实types。 在印刷声明的时候,需要完成工作来弄清楚。 在静态types语言中,types是完全已知的,因此可以增加运行时的性能。
一个推测:
在一种编译语言中,一个系统(编译器)可以看到所有强键入所需的代码。 口译员一般只能看到程序的一小部分,所以不能进行这种交叉检查。
但这并不是一条硬性的规则 – 制作一个强types的解释型语言是完全可能的,但是这将违背解释型语言的“宽松”的一般感觉。
有些语言是为了在非常规条件下完美运行,并且在特殊条件下遇到可怕的performance,因此非常强大的打字。 其他人只是为了平衡与额外的处理。
有时候,打字的方式还有很多。 以ActionScript为例。 3.0引入了更强的types,但是ECMAScript再次使您能够在运行时看到适合的类,并且ActionScript支持dynamic类。 非常整洁,但事实上他们说dynamic类不应该用在“标准”版本中,这意味着当你需要安全地玩时,这是一个“不”的事情。