Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pretty printing nested syntax quotes

Tags:

clojure

quote

Is there a way to print the result of evaluating nested syntax quotes in a legible manner as in SBCL? This would be useful when debugging nested syntax quotes when writing macros. For instance, in Clojure 1.8,

(let [x '(1 2)] ``(~~@x))

evaluates to

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

In SBCL 1.3.6, the equivalent expression

(let ((x '(1 2))) ``(,,@x))

evaluates to the much more legible

`(,1 ,2)

The difference becomes larger with more complex expressions. Are there any Clojure packages or other methods that can help with this situation? Currently, the best way I've found to debug complex syntax quotes is to convert them to Common Lisp but this is a rather ridiculous and slow approach.

like image 826
Qudit Avatar asked Nov 09 '22 05:11

Qudit


1 Answers

If you look the function syntaxQuote(Object form)in the LispReader Class of Clojure : 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)));

you'll see that `(~@'(some list)) is read as :

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

Which is evaluated by the REPL as :

=> (some list)

Just look at the effect of ` alone

`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)))))))))))))

And now adding the splicing

````~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

So what are the remarks to make

  1. Clojure writer doesn't render quotes as received by the reader macro so it gives (quote s) instead of 's. This is done for arrays, set, ...
  2. The back-tick macro reader doesn't make simplification on list/quote matching. Concatenation of only quoted lists and single entries should be executed immediately, giving

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

  3. The problem is even worse if you manage back-ticking on lists or arrays or sets... ;)

The problem is that `s is not 's.

If I remember in LISP `s gives 's as a result. ;)

A possibility to manage a simplified version can be viewed so

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

could be changed in the macro generation as

(seq 'user/s)

But!!! the result is a sequence, say a lazy sequence, not 'user/s.

Let's try on ````s. simplifying would give :

(seq '(clojure.core/seq 'user/s))

The result is equivalent to ''s, but it isn't the same object.

Another thing, we have to manage the toString of (quote ...) as '...

And contrary to LISP 's is s and `s is user/s !!!

And for macro management, it makes a lot of changes... Say 's is in the execution namespace, `s is in the compilation namespace, executed by the reader.

like image 64
Ivan Pierre Avatar answered Nov 15 '22 12:11

Ivan Pierre