为什么dynamic打字通常与口译语言联系在一起?
简单的问题:我在C ++ / Java等编译语言和Python / Javascript等解释语言中进行了大量编程(专业和个人)。 我个人发现,当我使用静态types语言进行编程时,我的代码几乎总是更加健壮。 但是,我遇到的几乎所有的解释语言都使用dynamictypes(PHP,Perl,Python等)。 我知道为什么编译语言使用静态types(大部分时间),但我无法弄清解释语言devise中对静态types的厌恶。
为什么陡峭的断开? 它是解释型语言本质的一部分吗? OOP?
有趣的问题。 顺便说一下,我是phc (PHP的编译器)的作者/维护者,并且正在做关于dynamic语言的编译器的博士学位,所以我希望我能提供一些见解。
我认为这里有一个错误的假设。 PHP,Perl,Python,Ruby,Lua等的作者没有devise“解释型语言”,他们devise了dynamic语言,并使用解释器来实现它们。 他们这样做是因为解释器比编译器要容易得多。
Java的第一个实现被解释了,它是一个静态types的语言。 解释器确实存在静态语言:Haskell和OCaml都有解释器,并且曾经是C语言的stream行解释器,但那是很久以前的事了。 他们是受欢迎的,因为他们允许REPL ,这可以使开发更容易。
也就是说,正如你所期望的那样,对dynamic语言社区的静态types感到厌恶。 他们认为由C,C ++和Java提供的静态types系统是冗长的,不值得。 我想我在一定程度上同意这一点。 用Python编程比C ++更有趣。
为了解决他人的观点:
-
dlamblin说 :“我从来没有觉得编译和解释有什么特别的地方,这意味着静态types的dynamic性。” 那么,你在那里是非常错误的。 编译dynamic语言非常困难。 大多数的
eval
语句需要考虑,在JavaScript和Ruby中广泛使用。 phc提前编译PHP,但是我们仍然需要一个运行时解释器来处理eval
。eval
也不能在优化编译器中静态分析,但是如果你不需要健壮的话,还是有一个很酷的技术 。 -
对damblin对Andrew Hare的回应:你当然可以在解释器中执行静态分析,并在运行之前发现错误,这正是Haskell的
ghci
所做的。 我期望在函数式语言中使用的解释器的风格要求这样做。 当然,兰布林是正确的说分析不是解释的一部分。 -
安德鲁·海尔的答案是以提问者错误的假设为基础的,同样也有错误的方法。 但是,他提出了一个有趣的问题:“dynamic语言的静态分析有多难?”。 非常非常困难。 基本上,你会得到一个博士来描述它是如何工作的,这正是我正在做的。 另见前面的观点。
-
到目前为止,最正确的答案是Ivo Wetzel 。 然而,他所描述的观点可以在运行时在编译器中处理,许多编译器对Lisp和Scheme都有这种types的dynamic绑定。 但是,是的,它很棘手。
解释型语言使用dynamictypes,因为没有进行静态分析的编译步骤。 编译语言在编译时进行静态分析,这意味着任何types的错误都会在开发人员工作时报告给开发人员。
如果你认为静态types的语言有一个编译器在执行的上下文之外强制执行types规则,那么就容易理解了。 解释型语言永远不会被静态分析,所以types规则必须由解释器在执行的上下文中执行。
我认为这是因为解释型语言的本质,他们希望是dynamic的,所以你可以在运行时改变事物。 由于这个原因,在下一行代码被执行之后,编译器从来不知道程序的状态是什么。
想象下面的情况(在Python中):
import random foo = 1 def doSomeStuffWithFoo(): global foo foo = random.randint(0, 1) def asign(): global foo if foo == 1: return 20 else: return "Test" def toBeStaticallyAnalyzed(): myValue = asign() # A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption myValue += 20 doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;) toBeStaticallyAnalyzed()
正如你所希望看到的,编译器在这种情况下是没有任何意义的。 实际上,它可能会警告你“myValue”可能不是一个数字。 但是,在JavaScript中,这将失败,因为如果“myValue”是一个string,20将被隐含地转换为一个string,因此不会发生错误。 所以你可能会得到成千上万的无用的警告,我不认为这是编译器的意图。
灵活性总是伴随着一个价格,你需要更深入地看看你的程序,或者更仔细地编程,换句话说,你是上述情况下的编译器。
所以你的解决scheme作为编译器? – 修正了一个“尝试:除了”:)
编译器+静态types=有效的机器代码
编译器+dynamictypes=低效的机器码
考虑下面的伪代码:
function foo(a, b) { return a+b }
一个静态语言将能够知道(通过声明或推论)a和b是整数,并将编译到
%reg = addi a,b
或者类似的东西,无论如何。
dynamic语言的编译器必须发出代码
1.检查他们types的a和b
2.处理每个案件或案件的组合
%reg1 = typeof a beq %reg1, int, a_int_case beq %reg1, float, a_float_case beq %reg1, string, a_string_case label a_int_case %reg1 = typeof b beq %reg1, int, a_int_b_int_case beq %reg1, float, a_int_b_float_case beq %reg1, string, a_int_b_string_case label a_int_b_int_case %out = addi a,b goto done label a_int_b_float_case %tmp = mkfloat a %out = addf %tmp,b goto done ... Etc. I can't finish
虽然您可以生成更智能的机器代码,但是您无法帮助生成大量的代码 – 这使得汇编不是dynamic语言的主要胜利。
由于口译员写起来容易得多,而编译对你也不是很好,所以为什么不写一个口译员呢?
(即时编译器实际上有types信息,可以直接编译到单个语句中,它们实际上比静态types系统有更多的信息,理论上可以做得更好,所有汇编程序都是模拟的;与真实代码的任何相似之处可以在真机上运行纯属巧合。)
也许这是因为我的主要解释语言之一是Perl,而我的一个编译语言是Objective-C,但是我从来没有强烈地感觉到编译与解释之间有什么特别之处,表明dynamic的静态types。
我认为很显然,双方都在看另一个,思考:“有一些优势。” 在几个应用程序中获得一些dynamictypes的灵活性比较容易,而维护静态types和强制执行的东西更容易。
我不同意Andrew Hare的解释 。 虽然纯粹的解释语言必须在预处理步骤中添加,因此不能纯粹解释为了在执行静态打字错误之前警告程序员,但是它并不排除在运行时抛出types错误。 所以缺乏编译并不意味着不会进行静态types检查。 但是由于在运行时获得一个types错误并不像在编译时或者在预检时那样有用,我可以看到静态types在这种情况下的“优势”看起来更麻烦,从而得到了dynamic打字带来的优势。
如果你从一开始就知道你更喜欢保持你的types是静态的,那么你就可以写出更好的更易维护的代码,而且当你devise你的解释型语言时,没有什么能阻止你将语言devise成静态types的。
引用解释型语言维基文章 “理论上讲,任何语言都可以被编译或解释,所以这个名称纯粹是因为通用的实现惯例,而不是一种语言的一些基本属性。”
有一个体面的维基文章只是打字。
我认为静态types使得编译器更容易编译,这是编译语言中的主要原因(如果不是唯一的话)。
对于解释型语言,假设variables没有types(只有值有),因为它们被认为不是必须适合的数据的放置者,而是为在堆上某处浮动的数据标记。
如果程序员想要的话,他总是可以断言该variables保存给定types的值(例如赋值)。 没有理由把它build成语言。 当然,这与编译语言的控制不同。
你可能有语言,你必须明确地声明每个variables的types,但是如果你不这样做更容易做一些有趣的事情,静态types需要程序员非常精心制作复杂的genericstypes。
另一方面。 你知道任何dynamictypes编译(静态,而不是JIT)语言吗?