动态语言有什么吸引人的地方?

81
最近似乎每个人都在跟风使用动态、非编译语言。我大部分只用过编译型、静态类型语言(如C、Java、.Net),对动态语言的经验仅限于ASP(VB Script)、JavaScript和PHP等技术。但这些技术给我留下了不好的印象,因为编译器通常会捕捉到例如拼写错误的变量名和把错误类型的值赋给变量等问题,而在动态语言中这些问题要等到运行时才能发现。即使在运行时发现了问题,你可能也没有注意到它,因为程序会创建一个新变量并分配默认值。我也从没看到过动态语言中的智能提示表现得很好,因为变量没有任何明确的类型。

我想知道的是,人们究竟觉得动态语言有什么吸引力?使用动态语言有哪些主要优势,这些优势难以在编译型语言中实现或者实现起来很困难?我觉得我们很早之前就已经认定像未编译的ASP页面抛出运行时异常这样的事情是个坏主意。为什么这种代码类型又重新兴起了?而且,至少在我看来,Ruby on Rails看起来并不像你在10年前不能用ASP实现的东西。


9
很遗憾(也很奇怪)看到这么少人捍卫动态语言。 - davidtbernal
11
既然这是获取某些东西的唯一途径,而且所有反对动态语言的人都在下面,我会在这里回答:毫无疑问,动态语言让你编写代码更快。我不必担心任何变量的类型,并且我也不必启动一个笨重的 IDE 来编写代码。因此,它更适合执行快速任务,因为静态类型系统的繁琐性使您需要告诉编译器 EVERYTHING,这会导致耗时更长。 - RCIX
2
C#程序员为何如此顽固狭隘? - Azeem.Butt
2
你还没有阅读过http://steve.yegge.googlepages.com/is-weak-typing-strong-enough这篇文章吧? - RCIX
5
变量名拼写错误成为问题的原因在于隐式变量声明,而非静态/动态语言。需要显式变量声明的动态语言(如Smalltalk)不会出现这个问题。 - Frank Shearar
显示剩余4条评论
32个回答

101

我认为原因是人们习惯于静态类型语言,这些语言具有非常有限和表达能力不足的类型系统。这些语言包括Java、C++、Pascal等。与其朝着更具表现力的类型系统和更好的类型推断方向发展(例如Haskell,甚至是SQL在某种程度上),有些人喜欢将所有“类型”信息都保留在头脑中(以及测试中),并完全放弃静态类型检查。

最终这样做给你带来的好处是不明确的。关于类型检查有许多误解,我经常遇到的两个误解如下:

谬论:动态语言不那么啰嗦。 误解在于类型信息等同于类型注释。这是完全不正确的。我们都知道类型注释很烦人。机器应该能够找出那些东西。实际上,在现代编译器中它也确实能找出来。下面是一个用两行Haskell编写的静态类型的快速排序(摘自haskell.org):

qsort []     = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)

这里是一个使用LISP实现的动态类型快速排序(来自swisspig.net):

(defun quicksort (lis) (if (null lis) nil
  (let* ((x (car lis)) (r (cdr lis)) (fn (lambda (a) (< a x))))
    (append (quicksort (remove-if-not fn r)) (list x)
      (quicksort (remove-if fn r))))))

这个Haskell的例子推翻了“静态类型语言通常很啰嗦”的假设。而LISP例子则反驳了“啰嗦的语言是静态类型的”这一假设。类型和冗长之间没有任何因果关系。你可以放心地不用考虑这点。

谬论:静态类型语言只能编译,不能解释。同样不正确。许多静态类型语言都有解释器。比如Scala解释器、Haskell的GHCi和Hugs解释器,当然SQL也一直以来既是静态类型又是解释型。

也许动态派想要的只是不必认真思考就能写出软件的自由。这样写出来的软件可能不正确也不健壮,但也许这并不重要。

就我个人而言,我认为那些为了购买一点暂时的自由而放弃类型安全的人,既不配得到自由,也不配得到类型安全。


26
牺牲类型安全以换取自由,既得不到二者。哦,是啊,太棒了,这篇文章的结尾非常出色。 - baash05
6
Lisp本身相当冗长,这与其动态类型无关...在Python中尝试一下。 def qsort(l):返回qsort([x for x in l [1:] if x <l [0]])+ l [0] + qsort([x for x in l [1:] if x> = l [0]])if l else l - fortran
13
确切地说,这就是重点。它与动态或静态类型无关。 - Apocalisp
9
我认为你的例子不太恰当。赞扬动态语言的人不太可能选择Lisp或Haskell。他们更可能选择Python或Ruby,而不是Java或C#。 - Corey D
8
有人认为冗长和类型性之间存在联系。但是,正如您所看到的,这种巧合纯属偶然。我选择这些语言正是因为它们不寻常。Haskell 的类型系统比大多数其他语言更加严格,因此它是静态类型语言的良好代表。LISP 是典型的动态语言,其他所有语言都必须模仿但永远无法复制。 - Apocalisp
显示剩余28条评论

70

不要忘记,在单元测试中编写的代码覆盖率需要达到10倍,以替代编译器提供的功能:D

我已经使用过动态语言并尝试过这种做法,但是我没有看到任何优势。


4
很高兴我不是唯一一个这样想的人,这让我晚上睡得更安心。 - erikkallen
这确实是静态类型优于动态类型的巨大优势。我不知道自己漏掉了多少次C++中的类型安全的typedef,只为了使编译器找到更多的错误。(Go编译器,加油! 给我找到更多的bug! :-) - Dimitri C.
4
@Garth:奇怪的定义。许多人可能不会同意这个定义。另一方面,大多数人都会同意编译器的类型检查器实现了很多(有时非常复杂的)测试。 - Konrad Rudolph
3
@yar,如果你不测试你的代码,你就会容易受到逻辑错误的影响。我已经在Python上工作了十年了,我认为我从来没有在生产环境中遇到过TypeError错误,但我遇到了很多逻辑错误。结论是:我不太需要静态类型检查,但绝对需要单元测试。 - Garth Kidd
@Garth T Kidd,很酷。我现在正在用Ruby进行测试,希望它能恢复甚至超越我对Java代码的信心,因为它具有编译时检查功能。开放的思维不太科学,但非常客观 :) - Dan Rosenstark
显示剩余2条评论

40

在阅读他人回复时,似乎有更多或更少的三个关于动态语言的论点:

1)代码不太冗长。我不认为这是有效的。一些动态语言比一些静态语言不那么冗长。但F#是静态类型的,但其中的静态类型并没有增加多少代码,如果有的话。它是隐式类型的,但那是另一回事。

2)“我最喜欢的动态语言X有我最喜欢的函数特性Y,因此动态更好”。不要混淆功能和动态(我不明白为什么必须这样说)。

3)在动态语言中,您可以立即看到结果。新闻:您也可以在Visual Studio中使用C#(自2005年以来)做到这一点。只需设置断点,在调试器中运行程序并在调试期间修改程序即可。我一直这样做,而且效果非常好。

就我个人而言,我非常支持静态类型,主要原因是可维护性。我有一个包含几万行JavaScript的系统,任何我想要进行的重构都需要花费大约半天的时间,因为(不存在的)编译器不会告诉我那个变量重命名搞砸了什么。而那是我自己编写的代码,我认为结构良好。我不想被委任负责一个等价的由其他人编写的动态系统的任务。

我想我会因此遭受大规模的贬值,但我愿意冒险。


引用:在动态语言中,您可以立即看到结果。新闻:自从2005年以来,您也可以在Visual Studio中使用C#来实现这一点。只需设置断点,在调试器中运行程序并在调试期间修改程序。我经常这样做,它完美地工作。这已经存在于Delphi中自1995年开始(?),可能在Turbo Pascal之前就已经有了(我不记得确切的时间)。 - No'am Newman
6
10000行的JavaScript?我认为这大概多了9000行,而我喜欢脚本语言... - Oz.
@No'am: 我知道。你也可以在Visual C++ 6中做到这一点(这实际上是我不转到C#直到VS2k5发布的主要原因)。如果有什么问题,这只会增加我的观点。 @Oz: 你怎么知道我的JS要做多少工作? - erikkallen
我认为那些喜欢立即看到变化效果的人也喜欢使用纯文本编辑器,而不是VS。各有所好。你可以考虑使用类似JSLint这样的工具。 - dlamblin
@dlamblin:为什么使用文本编辑器比使用集成开发环境更好?顺便说一句,我刚刚测试了Java的Eclipse,它也支持编辑和继续。Jslint,不用了,我改用Script#了。 - erikkallen
2
重构的观点很好。我真的开始喜欢Ruby用于快速原型设计和小型脚本,但是如果没有静态类型检查,我绝不会尝试在几个开发者之间维护一个大型产品。 - LoveMeSomeCode

19

除非与其他版本的VB进行比较,否则VBScript很糟糕。 PHP还不错,只要记住它是一个过度臃肿的模板语言。 现代JavaScript非常棒。真的。非常有趣。只要远离任何标记为“DHTML”的脚本即可。

我从未使用过不允许运行时错误的语言。在我看来,这在很大程度上是一个诱导人误入歧途的问题:编译器无法捕获所有的拼写错误,也无法验证意图。当需要明确类型时,显式类型很棒,但大多数时候不需要。在此处搜索有关通用类型或使用无符号类型作为索引变量是否是一个好选择的问题 - 大多数情况下,这些东西只会妨碍工作,并为人们提供了一些可以调整的旋钮以消磨时间。

但是,我还没有真正回答你的问题。为什么动态语言具有吸引力?因为经过一段时间后,编写代码变得乏味,您只想实现算法。您已经坐下来用笔计算出所有可能出现的问题场景并证明了它们是可解决的,现在唯一剩下的就是编写20行实现代码...和200行的样板代码使其编译。然后您意识到,您使用的类型系统并不反映您实际正在做的事情,而是某个人超级抽象的想法,即您可能正在做的事情,并且您早已放弃了编程,进入了一种沉迷于小物件调整的生活,这甚至会让虚构侦探阿德里安·蒙克感到羞耻。

那就是当你开始认真看待动态语言的时候。


有趣的东西...我会看看 Ruby 是否能够说服我。PHP 没有,但我感觉很多都是因为它的面向对象编程是事后加上去的。 - Dan Rosenstark
3
“二十行代码实现... 两百行样板使其编译”:我不同意这个说法。当然,在Java时代是正确的,但C# 3和Scala已经大大减少了所需的样板代码量。 - cdmckay
5
Java 的时代结束了吗? 开啤酒,准备庆祝 哦...等等... 是 C++。 - Shog9
2
"VBScript很糟糕,除非你将其与另一种VB语言进行比较。什么?你是说VBScript是Visual Basic的最佳变体吗?我一定误解了你。" - MusiGenesis

19

我是一名全职的.Net程序员,完全陷入了静态类型的C#编程之中。然而,我喜欢现代的JavaScript。

总体来说,我认为动态语言比静态类型语言更能简洁地表达你的意图,因为你花费更少的时间和空间定义你尝试表达的构建块,这在许多情况下是不言自明的。

我认为动态语言有多个类别。我没有回到用VBScript编写经典ASP页面的愿望。为了有用,我认为动态语言需要支持某种集合、列表或关联结构,在其核心表达对象(或可以传递为对象)并允许您构建更复杂的结构。(也许我们都只应该在LISP中编码……这是一个玩笑…)

在.Net圈子里,动态语言因为与VBScript和/或JavaScript相关联而声名狼藉。VBScript因为Kibbee所述的原因而令许多人想起噩梦-还记得使用CLng强制类型以确保获取32位整数的足够位吗?此外,我认为JavaScript仍被视为为所有浏览器编写不同的下拉菜单的浏览器语言。在那种情况下,问题不在语言本身,而在于各种浏览器对象模型。有趣的是,随着C#不断成熟,它看起来越来越动态了。我喜欢Lambda表达式、匿名对象和类型推断。它感觉每天都更像JavaScript。


1
我希望有人能够将文件处理、套接字和GUI库添加到JavaScript中,然后构建一个编译器......让JS可以在桌面上运行....... - UnkwnTech
2
http://code.google.com/p/jslibs/ - Breton
此外,一直以来都可以使用jscript编写Windows GUI应用程序。无论如何,很长一段时间以来都是这样的。有关更多信息,请参见“Windows HTA”-在HTA中运行一些额外的API,而在浏览器中则没有。仪表板小部件具有很强的功能。iPhone上的Web应用程序比大多数人认为的要强大得多。苹果已经在移动Safari中为浏览器JS提供了许多强大的API。 - Breton
http://www.commonjs.org/ - Xiong Chiamiov
+1 针对意图。虽然您的代码可能有一天会被翻译成静态语言,但动态语言(特别是Python)非常适合一次性任务和原型设计。 - new123456
@Unkwntech,看看Adobe AIR。 - Horacio Nuñez

18

这是从haskell.org上抽取的两行Haskell实现的静态类型快速排序:

qsort []     = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)

这里是一个用LISP编写的动态类型快速排序示例(来自swisspig.net):

(defun quicksort (lis) (if (null lis) nil
  (let* ((x (car lis)) (r (cdr lis)) (fn (lambda (a) (< a x))))
    (append (quicksort (remove-if-not fn r)) (list x)
      (quicksort (remove-if fn r))))))
我认为你在选择语言时有所偏见。Lisp以括号数量繁多而著称。更接近Haskell的语言是Python。
if len(L) <= 1: return L
return qsort([lt for lt in L[1:] if lt < L[0]]) + [L[0]] + qsort([ge for ge in L[1:] if ge >= L[0]])

这里的Python代码


25
这不是反驳,而是支持性论点。这表明语言的类型系统(或缺乏)对于它是否冗长或简洁几乎没有什么作用。 - Apocalisp
2
我同意Apocalisp的观点,冗长不取决于动态语言还是静态语言。我甚至会说,静态/动态类型对语言的冗长几乎没有影响。因此,这并不是针对静态类型阵营的破坏性反驳。 - BefittingTheorem
sort(@array); - James Anderson
Apocalisp的整个比较都是胡说八道。首先,快速排序(由Tony Hoare在原始论文中定义)是一种就地算法,专门设计为使用最少的额外空间,但Apocalisp使用了Haskell社区的变形的非就地版本,这浪费了渐进更多的内存,并且运行速度比真正的快速排序慢数百倍。Haskell难以表达真正的快速排序算法,因为它依赖于突变(!)。看看这些Haskell尝试,并回到我关于Haskell所谓的简洁性:http://www.haskell.org/haskellwiki/Introduction/Direct_Translation - J D
其次,你不能基于一个算法的两个实现来对冗长性做出任何强有力的陈述,尤其是针对其中一种语言进行了篡改。看看 APL、J、K、Mathematica 或其他任何简洁(=现代)动态类型语言。它们应该比任何静态类型语言更为简洁。类型推断缩小了差距,但仍然存在差距。 - J D

15

对于我来说,动态语言的优点在于代码变得更加易读,因为有更少的代码和类似Ruby块和Python列表推导式这样的函数技术。

但是我有点想念编译时检查(拼写错误确实会发生)和IDE自动完成。总的来说,代码量更少和可读性更高对我来说很划算。

另一个优点是语言通常是解释性/非编译性的。更改一些代码并立即看到结果。这真的是开发过程中的时间节省者。

最后但并非最不重要的是,我喜欢你可以启动一个控制台,尝试一些你不确定的东西,比如一个以前从未使用过的类或方法,看它的行为如何。控制台有许多用途,我会让你自己去发现。


我知道至少有一个Python IDE(即IDLE,它恰好随Python解释器的常规构建一起提供)确实具有自动完成功能,尽管仅在解释器窗口中声明的变量才具有此功能。 - JAB
1
@01:它使用了语言的常见结构。如果你了解该语言的基础知识,它相当易读。 - Esteban Küber
1
可读性与动态类型无关。例如,Scala的lambda通常比Ruby的块更短(并且可以说更具表现力),同样地,比较Haskell和Python的列表推导式也是如此。REPL控制台存在于F#,Scala,Haskell等语言中。快速加载更改后的代码到正在运行的应用程序是动态语言的优点。虽然有一些技术可以为静态语言实现这一点(例如JavaRebel)。 - Alexey
1
有趣的是,我发现 LESS 代码不太易读。首先,因为我经常无法使用我的 IDE 来查找声明和嵌入式文档等内容;其次,由于语法非常紧凑,我经常会忘记它到底是什么意思!我还会更加重视 IDE 自动完成的丧失。它不仅是救星,我认为它绝对增加了可维护性。 - spronkey
我认为动态语言最大的缺点是缺乏适当的IDE支持。与在Text Mate中瞎搞相比,Visual Studio和Eclipse可以将生产力提高至少5倍。 - siamii
显示剩余2条评论

12

你提到动态语言的缺点是完全合理的。不过请考虑以下几点:

  1. 动态语言不需要编译:只需运行它们。在大多数情况下,甚至可以在运行时重新加载文件而无需重新启动应用程序。
  2. 动态语言通常更简洁易读:你是否曾经查看过用静态语言实现的某个算法或程序,然后将其与 Ruby 或 Python 等的等效代码进行比较?一般情况下,您会看到代码行数减少了三倍。在动态语言中,许多支持代码是不必要的,这意味着最终结果更易读,并且更专注于实际问题本身。
  3. 不必担心类型问题:使用动态语言编程的一般方法是不必担心类型: 大多数情况下,正确类型的参数将被传递到您的方法中。偶尔会有人使用不同类型的参数,但仍然能够工作。当出现问题时,程序可能会被停止,但如果您进行了一些测试,这种情况很少发生。

刚开始时离开安全的静态类型世界可能会让人有些害怕,但对我来说,优点远远大于缺点,我再也没有回头看过去。


7
按照您的逻辑,我可以认为类似C#和Java这样的编译语言不需要编译,因为我只需在IDE上点击“播放”按钮即可运行它们。由于我没有注意到IDE正在为我编译,所以“根本无关紧要”。 - cdmckay
2
@cdmckay:你能连接到正在运行的C#/ Java程序并对其运行命令,修改或查询它正在运行的状态吗?许多动态语言允许运行时内省,而编译语言则不能。 - RHSeeger
4
@RHSeeger - 嗯,是的,你可以用Visual Studio完成所有这些操作。编辑并继续不仅限于动态语言。 - Greg Beech
4
@baash05,我认为你完全错过了这个回答的要点。1.意味着你可以更快地运行你所写的代码,无需等待编译器查看每个小改变的效果。2.不管你是否同意其影响,都将有更少的代码需要编写和阅读,这一事实是无可争议的。 - Fire Crow
1
这不是静态与动态的问题,而是过程式与函数式的问题。确实:Python(以及许多其他动态语言)比Java更加函数式。错误:这与动态类型没有任何关系。 - erikkallen
显示剩余7条评论

8
我认为对动态类型语言的“新发现之爱”与静态类型语言在绝对意义上更好或更差无关,而是与某些动态语言的流行有关。Ruby on Rails显然是引起动态语言复兴的重要现象。使Rails如此受欢迎并创造了许多从静态阵营转变过来的人的原因主要是:非常简洁和DRY的代码和配置。与需要大量XML配置的Java Web框架相比,这一点尤其正确。许多聪明的Java程序员也转向了Ruby和其他动态语言,甚至成为了它们的布道者。对我而言,三个不同的特点使得像Ruby或Python这样的动态语言更加简洁:
  1. 极简主义语法-最重要的是不需要类型注释,但同时语言设计者从一开始就将语言设计为简洁
  2. 内联函数语法(或lambda)-编写内联函数并将其作为变量传递使许多种类的代码更加简短。特别是对于列表/数组操作,这一点尤其正确。这个想法的根源显然是LISP。
  3. 元编程-元编程是Rails运行的重要组成部分。它产生了一种新的重构代码的方式,使您的库的客户端代码更加简洁。这也来自LISP。

这三个特点并不是动态语言专有的,但它们显然不存在于今天流行的静态语言Java和C#中。您可能会争辩说C#有第二个特点(委托),但我认为它根本没有被广泛使用,例如在列表操作中。

至于更高级的静态语言... Haskell是一种非常好的语言,它具有1和2,虽然没有3,但它的类型系统非常灵活,您可能不会发现缺少元编程限制了它。我认为您可以使用语言扩展在OCaml中在编译时进行元编程。Scala是一个非常新的补充,非常有前途。对于.NET阵营,F#也是如此。但是,这些语言的用户是少数,因此他们并没有真正促进编程语言格局的变化。事实上,我非常相信Ruby的受欢迎程度以积极的方式影响了像Haskell、OCaml、Scala和F#等其他动态语言的受欢迎程度。


7

个人认为,你所使用的大多数“动态”语言只是一般语言的差劣例子。

使用Python比使用C或Java更高效,这不仅仅是因为你必须进行编辑-编译-链接-运行的过程。我在Objective-C中也变得更加高效,但这可能更多地归功于框架。

不用说,我在任何这些语言中都比PHP更高产。该死的,我宁愿用Scheme或Prolog编码,也不要用PHP。(但最近我做的其实是更多的Prolog,所以这话要谨慎!)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接