ECMAScript是否真的是Lisp的一个方言?

55

我的一个朋友引起了我的注意,他提到了第四届欧洲Lisp研讨会的欢迎消息:

... 实现和应用 Lisp方言中的任何一种,包括 Common Lisp、Scheme、Emacs Lisp、 AutoLisp、ISLISP、Dylan、Clojure、ACL2、ECMAScript,...

然后问我ECMAScript是否真的是Lisp方言。 它是否真的可以被认为是?为什么?

是否有一组明确定义的标准来帮助我们检测一个语言是否是Lisp方言?或者是否使用了非常宽松的含义(在这种情况下,我们可以将Python、Perl、Haskell等添加到Lisp方言列表中吗?)


3
我听说它被称为“穿着C外衣的Lisp”,这很有道理。 - ryebr3ad
1
请参考这个 Stack Overflow 的问题,了解在 Haskell 上下文中的相同讨论。 - WReach
1
你质疑 ECMAscript 但不是 Dylan? :-) - Ken
1
@Ken:Dylan一直被誉为Lisp的一个方言,如果不是这样,Lispers早就会注意到了吧?(我的理解是s表达式一直被认为是相当抽象的,而更具体的语法已经计划了很长时间了...) - SamB
JavaScript 具有可变状态,但在函数式编程和特别是 Lisp 中,人们避免了那种难以理解的 hack 技巧,这主要源自图灵机的形象以及由于受限制的机器功率而试图接近机器编程。这是本质所在。因此,否。 - David Tonhofer
9个回答

55

Brendan Eich希望为Netscape创建一个类似Scheme的语言,但现实干涉了他的计划,他不得不用一些对“普通”人来说看起来模糊地像C和Java的东西,但实际上却像一个函数式语言。

个人认为将ECMAScript称为“Lisp”是一个不必要的扩大解释,但各有所好。真正的Lisp最重要的特点似乎是数据结构表示法和代码表示法相同,而这在ECMAScript(或Ruby或Python或任何其他动态函数式语言中都不成立,那些语言都 不是Lisp)。

免责声明:我没有Lisp凭证 :-)


3
完全同意。如果我们认为带有函数式编程特点的语言都是LISP方言,那么我们就必须将C#和VB.NET也包括在内。我真的不认为这两种语言算是LISP方言。我认为“代码即数据”是LISP的最显著特征:如果试图命名一个没有这种“代码即数据”灵活性的非LISP语言,那么人们就会明白为什么了。 - R. Martinho Fernandes
1
它可能在Lisp中最为人所知,并且可能是Lisp中的第一个,但它绝对不是Lisp独有的:http://en.wikipedia.org/wiki/Homoiconicity - Ken
1
@Ken 是的,谢谢。我今天看了一些相关内容。有趣的是,我读到一篇论文声称SNOBOL是同像的,这让我有点惊讶,因为我对它的记忆非常模糊 :-) - Pointy
2
@Sprague 是的,抱歉;我可能不应该使用“JSON”这个术语。也许动态类型和一级函数的组合是让人们想到Lisp的原因。 - Pointy
1
@R.MartinhoFernandes 一个非Lisp语言具有“代码即数据”的灵活性的语言是Forth。那么为什么连接语言如此强大而不是Lisps?因为它们融合了一些相同的简单性--它们是可扩展的,几乎没有保留字,几乎没有语法,具有同构性,并显示出强大的功能模式,允许轻松组合(函数管道),等等。其他连接语言,如Factor、Joy和Cat,似乎具有类似的属性。这就像晶体和普通岩石之间的区别--纯度很重要! - MicroservicesOnDDD
显示剩余6条评论

40

不是这样的。虽然你指出了其他非Lisp语言现在也有很多功能性根源,但Lisp仍具有一项特征,使其成为Lisp,即Lisp代码是以Lisp数据结构(同形式)编写的。这就是Lisp强大宏系统的基础,也是为什么它对非Lisp程序员看起来如此奇怪的原因。函数调用只是一个列表,其中列表中的第一个元素是函数的名称。

由于Lisp代码只是Lisp数据,因此可以使用元编程做一些非常强大的事情,在其他语言中无法做到。许多Lisps,甚至像Clojure这样的现代Lisp,都是作为一组宏来实现的。


3
这比页面上其他任何内容都更正确。 - Alexis Dumas

23

虽然我不会称JavaScript为Lisp,但在我看来,它比大多数主流语言(甚至是函数式语言)更接近Lisp的做法。

首先,就像Lisp一样,它本质上是一种简单的命令式语言,基于未类型化的λ演算,适合由REPL驱动。

其次,它很容易嵌入文本数据(包括以λ表达式形式的代码),因为它的子集等价于JSON。这是一种常见的Lisp模式。

第三,它的值和类型模型非常lispy。它在广义上支持面向对象编程,因为所有值都有一个身份概念,但在较窄的意义上,并不是特别面向对象。就像在Lisp中一样,对象是有类型且非常动态的。代码通常被分成函数单元,而不是类。

实际上,在JavaScript世界中,有几个(或多或少)最近的发展使得该语言有时感觉相当lispy。以jQuery为例,将CSS选择器作为子语言嵌入其中是一种相当Lisp的方法。或者考虑ECMAScript Harmony的元对象协议:它真的看起来像Common Lisp的直接移植(比Python或Ruby的元对象系统更像!)。此类例子还可以继续。

JavaScript确实缺少宏和一个合理的REPL实现与编辑器集成,这是不幸的。当然,它也受到其他语言的影响(并不一定是坏事)。尽管如此,在Lisp和JavaScript阵营之间存在着相当大的文化兼容性。其中一些可能是巧合的(例如JavaScript JIT编译的最近出现),一些则是系统性的,但它确实存在。


1
这些是通用的未类型化的λ演算方面,与Lisp无关。 - Paul Nathan
3
嗯...确实,方面#1和#3可能有所重叠。其他方面(命令性(!),嵌入子语言和文字数据,MOP,JIT编译)是否也源于未类型化的lambda演算核心?我会说它们不是,但话说回来,这本来就不是一个逐个特性比较的问题,而是一个支持文化亲近的论点。实际上,我并不排除这种亲近之所以存在的全部原因可能是由于两者都更强烈地基于(对未类型化的lambda演算的命令性解释)比大多数语言更多。 - Matthias Benkard

12
如果你把ECMAScript称为Lisp,基本上就是在断言任何动态语言都是Lisp。既然我们已经有了“动态语言”,你把“Lisp”简化为它的一个无用的同义词,而不是让它具有更具体的含义。
Lisp应该正确地指代具有特定属性的一种语言。
如果一个语言具备以下特点,那么它就是Lisp:
  • 其源代码是树形结构的数据,具有嵌套列表的直观打印表示法。每个可能的树结构都有对应表示法中的渲染,并且可以赋予其作为构造的含义;表示法本身无需扩展即可扩展语言。

  • 树形结构数据是该语言本身的主要数据结构,使得程序容易受到程序的操控。

  • 该语言具有符号数据类型。符号具有打印表示形式,并且是“内部化”的:当出现两个或多个相同的符号打印表示形式时,它们都代表同一个对象。

    • 符号对象的主要优点是它与所有其他符号不同。在Lisp程序的语义中,符号以各种方式与其他实体配对,并作为这些实体的名称。
    • 例如,Lisp的方言通常具有变量,就像其他语言一样。在Lisp中,变量由符号(内存中的对象)表示,而不是文本名称。当Lisp程序的一部分定义了某个变量a时,该a的语法是一个符号对象,而不是字符字符串"a",后者只是该符号用于打印的名称。对变量的引用,在程序的其它地方写作a的表达式,也是一个对象。由于符号的工作方式,它是相同的对象;这种对象的相同性将引用与定义连接在一起。对象的相同性可以在机器级别上实现为指针相等性。我们知道两个符号值是相同的,因为它们是指向堆中相同内存位置的指针(符号类型的对象)。
    • 以新Lisp方言为例,该方言具有大多数数据类型的非传统内存管理,包括嵌套列表,但符号例外,使它们以以上方式行为。如果没有这个功能,就不会是Lisp。引用:"newLISP中的对象(不包括符号和上下文)通过按值复制传递给其他用户定义的函数。因此,每个newLISP对象只需要一个引用。" [我强调]。即使对符号也进行按值复制传递,也会破坏它们的身份:接收符号的函数将无法获得原始符号,因此无法正确接收其身份。
  • Lisp语言中的复合表达式——不是简单的基本类型(如数字或字符串)——由一个简单列表组成,其第一个元素是表示操作的符号。剩余的元素(如果有)是参数表达式。Lisp方言应用某种评估策略,将表达式化简为一个值,并调用其可能具有的任何副作用。

  • 暂时认为,由二元单元组成的列表(这些单元保存一对值,并以特殊的空列表对象结束)应该被视为Lisp定义的一部分:能够通过将新项“cons”到前面来从现有列表中创建新列表,以及对列表的“first”和“rest”进行容易递归等。

然后我会停在这里。有些人认为Lisp系统必须是交互式的:提供一个具有监听器的环境,在其中一切都是可变的,并且可以随时重新定义等等。有些人认为Lisps必须具备头等函数:必须有一个lambda运算符等等。坚定的传统主义者甚至可能坚持认为必须有carcdr函数,点对表示法支持不正确的列表,列表由单元组成,并以特定符号nil表示空列表,并且还有一个布尔值false。坚持使用carcdr使得Scheme成为了Lisp,但是nil作为列表终止符和false则排除了它。什么,Scheme不是Lisp吗?
然而,我们越来越多地把“Lisp方言”的定义加入到内部,它越来越政治化;人们对他们最喜欢的方言(也许是他们自己创建的)因为某种技术上的技术性被排除在外而感到不满。坚持使用carcdr使得Scheme成为了Lisp,但是将nil作为列表终止符和false则排除了它。什么,Scheme不是Lisp吗?

所以,根据以上内容,ECMAScript并不是Lisp的方言。然而,一个ECMAScript实现可以包含功能,可以作为Lisp方言进行暴露,并且已经开发了许多这样的方言。出于某些情感原因希望将ECMAScript视为Lisp的人可能应该满足于此:即支持Lisp的语义存在,并且只需一个合适的接口来与该语义交互,这可以在ECMAScript中开发,并且可以与ECMAScript代码进行互操作。


我为这个答案的完整性和彻底性点了赞,但我强烈建议您纠正和重新表述一些部分,特别是关于carcdr讨论以及有关符号的第二个项目条目(第5个绝对项目符号,涉及变量及其名称; 非常难理解)。 - JMB

7
是的,确实如此。引用Crockford的话:
“JavaScript与Scheme有很多共同之处。它是一种动态语言。它具有灵活的数据类型(数组),可以轻松模拟s表达式。最重要的是,函数是lambda函数。
由于这种深层次的相似性,[递归编程入门指南]《The Little Schemer》中的所有函数都可以用JavaScript编写。”
关于同构性的问题,我建议您在搜索时将该词与JavaScript一起使用。说它“不是同构的”是正确的,但并不是故事的结束。

这是一个非常好的链接 - 但尽管我喜欢它的相似之处,我不认为相似足以将JavaScript归类为方言。然而,在JavaScript内部确实有一颗Lisp式的心脏在跳动。 - icc97

6

不,不是这样。

要被认为是Lisp语言,必须是同构的,而ECMAscript并不是。


6
不是方言。我在70年代学习了LISP,自那以后就没有再使用过它,但最近我学习JavaScript时发现自己觉得它类似于LISP。我认为这是由于两个因素造成的:(1)JSON是一种类似列表的关联结构,(2)似乎JS的'对象'本质上是JSON。因此,即使您不像写LISP一样在列表中编写JS程序,您也几乎可以这样做。
因此,我的回答是,在使用JavaScript时,熟悉LISP的程序员会想起它们。诸如“JS = Java服装中的LISP”之类的陈述只是表达这种感觉。我相信这就是全部内容。

4
“方言”这个词有点牵强。不过,作为一个学习并使用过Python、JavaScript和Scheme的人来说,显然,与Python相比,JavaScript(Coffeescript可能更是如此)的感觉更像Lisp语言。
至于欧洲Lisp研讨会为什么要把JavaScript描绘成一种Lisp语言,很明显,他们想要借助JavaScript的流行度,因为它的程序员数量远远超过了他们列表中所有其他Lisp方言的总和。

4
我认为ECMAScript就像英语是法语的方言一样,是LISP的一种方言。虽然它们有共同之处,但你仅凭另一种语言的知识很难完成任务。
我觉得很有趣的是,在第四届欧洲Lisp研讨���上,只有三个主题演讲中的一个与Lisp直接相关(其他两个是关于x86 / JVM / Python和Scala的)。

英语更像是德语的方言,但我离题了。 - kd4ttc

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