简单的问题:我用 C++/Java 等编译语言和 Python/Javascript 等解释语言做了很多编程(专业和个人)。我个人发现,当我使用静态类型语言进行编程时,我的代码几乎总是更加健壮。但是,我遇到的几乎所有解释语言都使用动态类型(PHP、Perl、Python 等)。我知道为什么编译语言使用静态类型(大部分时间),但我无法弄清楚解释语言设计中对静态类型的厌恶。
为什么陡峭的断开连接?它是解释语言本质的一部分吗?哎呀?
简单的问题:我用 C++/Java 等编译语言和 Python/Javascript 等解释语言做了很多编程(专业和个人)。我个人发现,当我使用静态类型语言进行编程时,我的代码几乎总是更加健壮。但是,我遇到的几乎所有解释语言都使用动态类型(PHP、Perl、Python 等)。我知道为什么编译语言使用静态类型(大部分时间),但我无法弄清楚解释语言设计中对静态类型的厌恶。
为什么陡峭的断开连接?它是解释语言本质的一部分吗?哎呀?
有趣的问题。顺便说一句,我是phc(PHP 编译器)的作者/维护者,并且正在攻读动态语言编译器的博士学位,所以我希望我能提供一些见解。
我认为这里有一个错误的假设。PHP、Perl、Python、Ruby、Lua 等的作者没有设计“解释语言”,他们设计了动态语言,并使用解释器来实现它们。他们这样做是因为解释器比编译器更容易编写。
Java 的第一个实现是解释型的,它是一种静态类型语言。静态语言确实存在解释器:Haskell 和 OCaml 都有解释器,并且曾经有一个流行的 C 解释器,但那是很久以前的事了。它们很受欢迎,因为它们允许REPL,这可以使开发更容易。
也就是说,正如您所期望的那样,动态语言社区中存在对静态类型的厌恶。他们认为 C、C++ 和 Java 提供的静态类型系统很冗长,不值得努力。我想我在一定程度上同意这一点。Python 编程比 C++ 有趣得多。
为了解决其他人的观点:
dlamblin 说:“我从来没有强烈地感觉到编译与解释之间有什么特别之处,表明动态类型优于静态类型。” 好吧,你大错特错了。动态语言的编译非常困难。主要是eval
要考虑的语句,它在 Javascript 和 Ruby 中广泛使用。phc 会提前编译 PHP,但我们仍然需要一个运行时解释器来处理eval
s。eval
也不能在优化编译器中进行静态分析,但如果您不需要健全性,有一种很酷的技术。
对于 damblin 对Andrew Hare的回应:您当然可以在解释器中执行静态分析,并在运行时之前发现错误,这正是 Haskellghci
所做的。我希望函数式语言中使用的解释器风格需要这个。dlamblin 说分析不是解释的一部分当然是正确的。
Andrew Hare 的回答是基于提问者的错误假设,并且同样有错误的方式。然而,他提出了一个有趣的问题:“动态语言的静态分析有多难?”。非常非常难。基本上,您将获得描述其工作原理的博士学位,这正是我正在做的事情。另见上一点。
迄今为止最正确的答案是Ivo Wetzel的答案。然而,他所描述的点可以在编译器中在运行时处理,并且存在许多具有这种动态绑定类型的 Lisp 和 Scheme 编译器。但是,是的,它很棘手。
我认为这是因为解释语言的性质,它们希望是动态的,所以你可以在运行时改变事情。因此,在执行下一行代码后,编译器永远不会确切知道程序的状态。
想象以下场景(在 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”是一个字符串,20 也会被隐式转换为一个字符串,因此不会发生错误。所以你可能会得到成千上万个无用的警告,我不认为这是编译器的意图。
灵活性总是有代价的,你需要更深入地了解你的程序,或者更仔细地编写程序,换句话说,你是上述情况下的编译器。
那么您作为编译器的解决方案是什么?- 用“try:except”修复它:)
解释语言使用动态类型,因为没有编译步骤来进行静态分析。编译语言在编译时进行静态分析,这意味着任何类型错误都会在它们工作时报告给开发人员。
如果您认为静态类型语言具有在执行上下文之外强制执行类型规则的编译器,则更容易理解。解释型语言永远不会静态分析,因此类型规则必须由解释器在执行上下文中强制执行。
编译器 + 静态类型 = 高效的机器代码
编译器 + 动态类型 = 低效的机器代码
考虑以下伪代码:
function foo(a, b) {
return a+b
}
静态语言将能够(通过声明或推断)知道 a 和 b 是整数,并将编译为
%reg = addi a,b
或类似的东西,无论如何。
动态语言的编译器必须将代码发送到
1. 检查 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
虽然您可以生成比这更智能的机器代码,但您无法帮助生成大量代码——这使得编译不是动态语言的主要胜利。
既然解释器更容易编写,而且编译对你没有多大好处,为什么不写一个解释器呢?
(即时编译器实际上具有类型信息,并且可以直接编译到单个语句。它们实际上比静态类型系统具有更多信息,并且理论上可以做得更好。所有的汇编程序都是模拟的;与真实代码的任何相似之处能在真机上运行纯属巧合。)
也许是因为我的主要解释语言之一是 Perl,而我的编译语言之一是 Objective-C,但我从未强烈感觉到编译与解释之间有什么特别之处,表明动态类型优于静态类型。
我认为很明显,双方都在看着对方,并在想,“这样做有一些优势。” 在多个应用程序中更容易获得一些动态类型的灵活性,同时更容易维护静态类型和强制执行的内容。
不过,我不同意Andrew Hare 的解释。虽然纯解释型语言必须添加预处理步骤,因此不能纯解释型以在执行静态类型错误之前警告程序员,但它并不排除在运行时引发类型错误。所以缺少编译并不意味着不会发生静态类型检查。但是由于在运行时出现类型错误不如在编译时或在预检期间出现类型错误有用,我可以看到在这种情况下静态类型的“优势”似乎更令人讨厌,从而被抛弃,取而代之的是动态类型带来的优势。
如果你从一开始就知道你更喜欢保持你的类型是静态的,因为你个人编写了更好的更易于维护的代码,并且你正在设计你的解释型语言,那么没有什么可以阻止你将语言设计为静态类型的语言。
引用解释语言 wiki 文章“理论上,任何语言都可以被编译或解释,所以这个名称的应用纯粹是因为常见的实现实践,而不是一种语言的某些基本属性。” 有一篇关于打字
的不错的维基文章。
动态类型的解释语言为您的编程方式提供更多自由。它允许元编程是可行的。它允许在运行时创建变量。允许在运行期间的任何给定时间创建匿名哈希和匿名数组,而无需事先声明任何内容。它允许将未确定的信息输入到散列中,而无需事先声明所有键。您可以从未确定的随机输入创建子例程。您也可以提供可以动态运行的程序代码。解释性语言从限制编程的链条中解放出来。您仅限于使用静态类型语言在源文件中键入的内容。在动态类型语言中,您可以事半功倍。
今天制造的大多数机器人更多地处理解释语言,因为需要在运行时确定信息,并且需要制作新的变量来在运行时存储这些信息。机器学习基于被解释的信息。作为人类,我们自己就是解释者,这就是机器人被这样设计的原因。未来真的是被诠释了。当然,您需要静态类型的语言来构建解释器,因此静态类型的语言永远不会消失,除非将来用汇编代码构建解释器。如今,大多数解释器都建立在静态类型语言之上。
口译语言在动态环境中表现出色。如果您可以在运行时解释新的代码/信息,那为什么不呢。如果您真的擅长动态编程,那么您可以创建可以创建变量和散列的代码,而无需输入所有内容。如果您处理大量数据,则可以大大减少行数。您可以使用数据转储器打印出所有信息,因为解释型语言通常会在运行时跟踪变量的类型,从而实现这一点。你不能在准系统 C++ 中做到这一点。c++ 和 c 唯一知道发生了什么的时间是在编译时。之后你自己,除非你自己实现一些东西。
这些天谁愿意被源文件束缚得如此之多,尤其是当您在动态环境中工作时。你所做的一切都在限制你的潜力。一旦你深入了解动态解释的代码并回到任何静态类型的语言,你会发现更难简化你的代码,因为你仍在以无限的思维方式思考。你的思想需要重新回到被限制在源文件中输入的内容上。
以编程风格的方式:静态类型的代码产生静态结果。动态类型代码产生动态或静态结果。
If your going to be programming something that never changes behavior other than what is known then statically typed languages are great for that. If your dealing with dynamic behavior then dynamically typed languages are better suited for those cases. Everything depends on the situation mostly.
Every language has their ups and downs. Just gotta choose and pick wisely.
我认为静态类型使编译器更容易,这是它出现在编译语言中的主要原因(如果不是唯一的话)。
对于解释型语言,更容易假设变量没有类型(只有值有),因为它们被认为不是必须适合内部的数据的放置器,而是浮动在堆上某处的数据的标签。
如果程序员想要,他总是可以断言该变量包含给定类型的值(例如在赋值时)。没有理由将其构建到语言中。当然,这与编译语言所拥有的控制不同。
您可能有一种语言,您必须在其中显式声明每个变量的类型,但如果您不这样做,那么使用静态类型需要程序员非常精心制作的复杂泛型类型来做有趣的事情会容易得多。
另一方面。你知道任何动态类型的编译(静态,不是 JIT)语言吗?