漂亮打印嵌套语法引用

6
有没有一种方法可以像SBCL一样以易读的方式打印评估嵌套语法引号的结果?这在编写宏时调试嵌套语法引号时非常有用。例如,在Clojure 1.8中,
(let [x '(1 2)] ``(~~@x))

评估为

(clojure.core/seq (clojure.core/concat (clojure.core/list 1 2)))

在SBCL 1.3.6中,等价表达式为:
(let ((x '(1 2))) ``(,,@x))

评估为更易读的

`(,1 ,2)

随着表达式的复杂程度增加,差异变得更大。是否有任何Clojure软件包或其他方法可以帮助解决这种情况?目前,我发现调试复杂语法引用的最佳方法是将它们转换为Common Lisp,但这是一种相当荒谬和缓慢的方法。

1个回答

2
如果你看一下Clojure中LispReader类的函数syntaxQuote(Object form)的语法: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LispReader.java#L1011
ISeq seq = RT.seq(form);
if(seq == null)
  ret = RT.cons(LIST,null);
else
  ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));

你会发现`(~@'(some list))被解读为:

(clojure.core/seq
  (clojure.core/concat
    (clojure.core/list (quote clojure.core/some))
    (clojure.core/list (quote clojure.core/list))))

这段代码在 REPL 中的评估结果为:

=> (some list)

仅看 ` 的效果就可以了解它的作用

`s => user/s ; it's the ref of the symbol ok
``s 
=> (quote user/s) ; it's the quoted ref 
```s
=> (clojure.core/seq ; now we manage a back-tick on a list
     (clojure.core/concat
       (clojure.core/list (quote quote)) 
       (clojure.core/list (quote user/s))))
````s
=> 
(clojure.core/seq ; oups! always a list we add a layer
 (clojure.core/concat
  (clojure.core/list (quote clojure.core/seq))
  (clojure.core/list
   (clojure.core/seq
    (clojure.core/concat
     (clojure.core/list (quote clojure.core/concat))
     (clojure.core/list
      (clojure.core/seq
       (clojure.core/concat
        (clojure.core/list (quote clojure.core/list))
        (clojure.core/list
         (clojure.core/seq
          (clojure.core/concat
           (clojure.core/list (quote quote))
           (clojure.core/list (quote quote))))))))
     (clojure.core/list
      (clojure.core/seq
       (clojure.core/concat
        (clojure.core/list (quote clojure.core/list))
        (clojure.core/list
         (clojure.core/seq
          (clojure.core/concat
           (clojure.core/list (quote quote))
           (clojure.core/list (quote user/s)))))))))))))

现在需要添加拼接功能。
````~s => (clojure.core/seq ; same as ```s
            (clojure.core/concat
              (clojure.core/list (quote quote)) 
              (clojure.core/list (quote user/s))))
````~~s => (quote user/s) ; same as ``s

所以,有哪些需要注意的事项呢?

  1. Clojure编写者不会像读取宏一样呈现引号,因此对于数组、集合等,它会给出(quote s)而不是's
  2. 反引号宏读取器不会简化列表/引号匹配。仅引用的列表和单个条目的连接应立即执行,得到

    ````~~s => ''s.

  3. 如果您在列表、数组或集合上进行反引号处理,问题会变得更加严重... ;)

问题在于`s`不是`'s`。

如果我没记错,在LISP中,`s`会作为结果给出`'s`。;)

管理精简版本的一种可能性如下所示

(clojure.core/seq
  (clojure.core/concat
    (clojure.core/list (quote quote)) 
    (clojure.core/list (quote user/s))))

在宏生成过程中可能会更改

(seq 'user/s)

但是!!!结果是一个序列,可以说是一个惰性序列,而不是'user/s'。
让我们尝试在````s上进行。简化后会得到:
(seq '(clojure.core/seq 'user/s))

结果等价于''的,但它不是同一个对象。
另一件事,我们必须将(引文...)的toString处理为“...”。
与LISP相反,'的s和` s是user/s !!!
对于宏管理,它会发生很多变化......说's在执行命名空间中,`s在编译命名空间中,由阅读器执行。

我猜你的意思是\`(some list)而不是`(some list)。这可能会被计算为`(clojure.core/some clojure.core/list)`。由于符号已经完全限定,所以符号解析不是问题。 - Qudit
@Qudit 我修改了回应以更精确地理解问题。 - Ivan Pierre
当然,Clojure从嵌套语法引用中生成的代码非常丑陋,但并没有根本性的原因阻止它(或一个漂亮的打印语法引用包)做类似于SBCL的事情。语法引用只需要扩展为表现出特定行为的代码即可。对于这个目的,Clojure生成的特定代码并没有什么特别之处。 - Qudit
我添加了一些补充内容,因为在注释中格式不可读。 - Ivan Pierre
我希望我没有错过解释,因为这可能有点令人困惑。这就是为什么宏管理可能会导致非显而易见的问题。反引号是一个读取宏,所以转换是在读取时完成的,编译后的宏已经具有了转换后的内容,如果存在多个嵌套级别,则可能会令人困惑。引用不存在...它只是值本身,引用是给程序员看的,程序只有值。 - Ivan Pierre
我理解 Clojure 中的语法引用。问题是是否有人有一种以可读的方式打印它们进行调试的方法。目前,我通过将复杂的 Clojure 语法引用转换为 Common Lisp 进行调试。(是的,我知道这不是同一个语言,但通常足够接近于调试目的。)显然,这是一种笨拙的方法。肯定有更好的方法。 - Qudit

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