Haskell中类似于Lisp反引号和剪接的语法

3
在某些Lisp方言(例如Elisp, Common Lisp),有一种称为反引号的特性。
它允许在构造列表时评估或插入其中一些元素。例如:
 `(1 2 (3 (+ 4 5))) 
  ⇒ (1 2 (3 (+ 4 5)))  ; just quoted unevaluated list

 `(1 2 (3 ,(+ 4 5)))
  ⇒ (1 2 (3 9))     ; (+ 4 5) has been evaluated

 `(1 2 ,@(list 3 (+ 4 5)))
  ⇒ (1 2 3 9)       ; (3 9) has been spliced into the list

我猜,在 Haskell 中,某些反引号的子集可能如下所示:
 [backquote| 1, 2, @$(replicate 2 3), 2 + 2 |]
 ⇒ [1, 2, 3, 3, 4]

我想知道,是否可以像这样将列表拼接起来,并且它是否已经实现。

1
评论不适合进行长时间的讨论;此对话已被移至聊天室 - Samuel Liew
1个回答

3

评论区似乎有些跑题了。无论如何,我对此有不同的看法,因此让我提供一个答案。

我认为Haskell已经有了类似于反引号的功能,而您可能在自己的Haskell编程中广泛使用它,而没有意识到它。

您已经将Lisp列表和Haskell列表进行了比较,但是在Lisp中,S表达式(即“带有原子”的对,特别是具有原子符号的对)是一种灵活且普遍的数据结构,不仅用于表示Lisp代码,而且作为任何复杂结构化数据的首选表示,因此大多数Lisp程序花费很多时间生成和操作这些结构,因此S表达式“文字”在Lisp代码中很常见。对于S表达式“几乎字面量”,其中需要计算一些子表达式,使用反引号机制比尝试使用像conslistappend等函数构建由较小的文字和求值部分组成的表达式更方便。

与此相比,Haskell中的情况则完全不同-- Haskell列表在Haskell代码中确实很受欢迎,并且是表示同质序列的首选结构,但它们提供的灵活性只是S表达式的一小部分。代数数据类型(ADT)是Haskell中相应的普遍数据结构。

就像Lisp使用它的S表达式一样,Haskell程序花费大量时间生成和操作ADT,并且Haskell也有方便的语法用于ADT字面量和“几乎文字”。 它们都统一到一个“函数应用”语法中,通过使用构造函数(以大写字母开头的标识符或以冒号为前缀的中缀运算符)与非构造函数(以小写字母开头的标识符或不带冒号前缀的中缀运算符)来区分文字和求值部分。 当然,对于某些构造函数(例如列表和元组),还有一些其他的语法。

例如,请比较Lisp和Haskell中以下反引号表达式:

;; Lisp
(setq baz `(node ,id
                 (node ,(+ id 1) ,left-tree leaf)
                 (node ,(+ id 2) leaf ,right-tree)))
-- Haskell
baz = Node id (Node (id + 1) left_tree Leaf) (Node (id + 2) Leaf right_tree)

在这个“几乎按字面意思”的Haskell版本中,Node和Leaf构造函数表示引用部分;left-tree、right-tree和+中缀表达式表示计算部分,它们在语法上可以通过构造函数和非构造函数的通常规则进行区分。
当然,与此完全分开的是一种Template Haskell机制,它直接在编译时操作Haskell代码片段。虽然该代码被表示为一个ADT,原则上可以使用与其他ADT相同的“几乎按字面意思”的语法编写,但涉及的ADT非常笨重,看起来与底层Haskell代码毫不相似。因此,Template Haskell提供了更经典的反引号语法。

1
谢谢你的出色回答,但你完全关注了反引号的半评估特性,在Haskell中有点琐碎。然而,最有趣的部分是拼接。我在讨论中提到了它,但我想我会编辑问题以澄清。 - grepcake
如果那是反引号,那么没有反引号的Lisp也有反引号。看!(make-node id (make-node (+ id 1) left-tree 'leaf) (make-node (+ id 2) ...) ...). - Kaz
Node和Leaf构造函数代表引用的部分,但不是作为符号。 - Kaz
1
这看起来像是一场漫长而痛苦的“评论讨论”的开端,因此请将这个澄清性的言论视为我对此话题的最后一句话。依我之见,Haskell 构造函数和函数是完全不同的对象,尽管它们有些表面上的相似之处,这就是我要提出的观点。一个构造函数更像一个(引用的)Lisp 符号,而不是 Lisp 函数——把它当作一个“自引用”的符号。所以,在 Haskell 中,你可以自由使用反引号,因为自引用符号和函数之间有一种语法上的区别。 - K. A. Buhr

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