【问题标题】:Is Clojure less homoiconic than other lisps? [closed]Clojure 比其他 lisps 的同音性更低吗? [关闭]
【发布时间】:2021-04-23 04:00:15
【问题描述】:

我记得在Clojure for Lisp Programmers videos 中重复的一个说法是,早期 Lisps,尤其是 Common Lisp 的一个很大的弱点,就是过多地与 Lisps 的列表结构结合,尤其是 cons 单元格。您可以在链接视频的第 25 分钟处找到一次这种说法,但我确信我记得在该系列的其他地方听到过这种说法。重要的是,在视频的同一点,我们看到了这张幻灯片,向我们展示了 Clojure 有许多其他的数据结构,而不仅仅是老式的 Lispy 列表: 这让我很困扰。我对 Lisp 的了解非常有限,但我一直被告知,其传奇的元可编程性的一个关键要素是一切——是的,一切——都是一个列表,而 this is what prevents the sort of errors that we get when trying to metaprogram other languages。这是否表明通过添加新的一流数据结构,Clojure 降低了其同质性,从而使其元编程比其他 Lisp 更困难,例如 Common Lisp?

【问题讨论】:

  • 这是基于意见的问题的典型示例。它应该这样关闭:这是一个很好的问题,但它不适合 SO。
  • @tfb 你不相信同音性是一个可测量的量吗?
  • 同音性是一个位:一种语言的源代码可以作为该语言的(预定义)数据结构处理,或者不能。其他一切都是意见。
  • Clojure 与 Lisp 相差甚远;它是一种用于操作不可变、持久序列的领域特定语言,其灵感来自 Lisp 语法及其元编程概念的大幅修改概念。
  • 元编程的唯一要求始终是能够在编译时构造 AST。 AST 可以是任何东西,不一定是一堆 cons 细胞。例如,方案“语法对象”根本不是列表。您可以将它们转换为列表并返回,但这不是一个好主意,因为您会丢失位置元数据。

标签: clojure lisp common-lisp metaprogramming homoiconicity


【解决方案1】:

在 Clojure 中,除了列表之外,某些语言结构所需的额外数据结构中唯一一个是向量,它们位于众所周知的位置,例如函数的参数序列周围,或符号/ let 的表达式对。它们都可以用于数据字面量,但与始终在列表中的函数调用和宏调用相比,数据字面量在编写 Clojure 宏时不太常见。

我不知道 Clojure 中有什么东西会使编写宏比 Common Lisp 更困难,并且有一些与 Clojure 不同的特性可以使它更容易一些,例如 Clojure 反引号表达式的行为默认将命名空间限定符号,这通常是您想要防止意外“捕获”名称的原因。

【讨论】:

  • 语言结构经常需要 FWIW 关键字和映射,尤其是元数据。
【解决方案2】:

让我们把“同音性”这个词分开。

  • homo-,意思是“相同”
  • 标志性,这里的意思是“代表”
  • -ity,意思是“具有这种属性的东西”

使宏成为可能(或至少更容易)的质量是语言本身使用与表示其他对象相同的数据结构来表示。请注意,这个词不是“单一主义”或“一切都是列表”。由于 Clojure 使用相同的数据结构来描述其语法,就像它在运行时描述其他值一样,因此没有理由声称它不是同音字。

有趣的是,我可能会说 Clojure 最不具有同音性的方式之一是它实际上与旧 lisps 共享的一个特性:它使用符号来表示源代码。在其他 lisps 中,这是非常自然的,因为符号用于许多其他事情。但是,Clojure 有关键字,它们更常用于在运行时为数据命名。符号很少使用;我什至可以说它们的主要用途是表示源代码元素!但是 Clojure 用来表示源代码元素的所有其他特性也经常用于表示其他事物:序列、向量、映射、字符串、数字、关键字、偶尔的集合,以及可能我没有想到的其他一些小众东西现在。

【讨论】:

  • 所以数据结构和对象的表示越少,编写宏就越容易。 Clojure 的独特之处不仅在于它使用符号,还在于它同时使用符号和关键字。 Clojure 对源代码使用与通常编码的数据结构相同的符号。我理解对吗?我只使用过宏(昨天刚刚为->> 得到了“啊哈”),但我为自己学会编写自己的那一天感到兴奋:)
  • 我不认为“最少”是一个很好的外卖。没有人在 lambda 演算中编写真正的程序(宏或非宏)是有原因的。重要的是,如果您习惯了该语言的非宏部分,源代码以易于使用和生成的格式表示。
【解决方案3】:

是不是一切——是的,一切——都是一个列表

这对 Lisp 来说从来都不是这样

CL-USER 1 > (defun what-is-it? (thing)
              (format t "~%~s is of type ~a.~%" thing (type-of thing))
              (format t "It is ~:[not ~;~]a list.~%" (listp thing))
              (values))
WHAT-IS-IT?

CL-USER 2 > (what-is-it? "hello world")

"hello world" is of type SIMPLE-TEXT-STRING.
It is not a list.

CL-USER 3 > (what-is-it? #2a((0 1) (2 3)))

#2A((0 1) (2 3)) is of type (SIMPLE-ARRAY T (2 2)).
It is not a list.

CL-USER 4 > (defstruct foo bar baz)
FOO

CL-USER 5 > (what-is-it? #S(foo :bar oops :baz zoom))

#S(FOO :BAR OOPS :BAZ ZOOM) is of type FOO.
It is not a list.

CL-USER 6 > (what-is-it? 23749287349723/840283423)

23749287349723/840283423 is of type RATIO.
It is not a list.

由于 Lisp 是一种可编程编程语言,我们可以为非列表数据类型添加外部表示:

为 FRAME 类添加原始符号。

CL-USER 10 > (defclass frame () (slots))
#<STANDARD-CLASS FRAME 4210359BEB>

打印机:

CL-USER 11 > (defmethod print-object ((o frame) stream)
               (format stream "[~{~A~^ ~}]"
                       (when (and (slot-boundp o 'slots)
                                  (slot-value o 'slots))
                         (slot-value o 'slots))))
#<STANDARD-METHOD PRINT-OBJECT NIL (FRAME T) 40200011C3>

读者:

CL-USER 12 > (set-macro-character
               #\[
               (lambda (stream char)
                 (let ((slots (read-delimited-list #\] stream))
                       (o (make-instance 'frame)))
                   (when slots
                     (setf (slot-value o 'slots) slots))
                   o)))
T

CL-USER 13 > (set-syntax-from-char #\] #\))
T

现在我们可以读取/打印这些对象了:

CL-USER 14 > [a b]
[A B]

CL-USER 15 > (what-is-it? [a b])

[A B] is of type FRAME.
It is not a list.

【讨论】:

  • “Lisp 中的一切都是列表”实际上比同样流行的“Haskell 中的一切都是函数”还要错误。
【解决方案4】:

正如之前的回答者所指出的,“同质性”只不过是著名的“codedata”:一种编程语言的代码由该语言的数据结构 100% 组成,因此很容易构建- 能够使用通常用于操作数据结构的语言中的函数进行操作。

由于 Clojure 数据结构包括列表、向量、映射...(您列出的那些),并且由于 Clojure 代码是由列表、向量、映射组成的...Clojure 代码是同形的。

Common Lisp 和其他 lisp 也是如此。

Common Lisp 还包含列表、向量、哈希表等。 然而,Common Lisp 的代码比 Clojure 更严格地坚持列表(函数参数在参数列表中,而不是在 Clojure 中的向量中)。 因此,您可以更一致地使用函数来操作列表以进行元编程(宏)。

这是否比 Clojure “更”谐音是一个哲学问题。 但与 Clojure 相比,您绝对需要一组更小的函数来操作代码。

【讨论】:

    猜你喜欢
    • 2021-05-09
    • 2011-08-25
    • 2021-06-07
    • 2011-04-24
    • 2018-10-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多