Продолжаю пилить свою имплементацию r5rs на Java.
Наконец дошли руки до quasiquote/unquote/unquote-splicing.
Скажу честно - это кошмар какой-то. Читаешь и, вроде, все более-менее понятно. Довльно простые штуки.
Но потом оказывается, что там стопицот особых случаев. И код превращается в непонятную кашу сплошных if-else'в (не говоря уже о том, что в разных Схемах quasiquote работат немного по-разному).
А, да, всю эту quasi-кашу делаю через list и append (http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf)
Так вот, оно вроде как работает, практически все тесты проходят. Но возник вопрос по одному тесту:
`(1 ,@())
Как эта штука должна работать?
Guile ругается:
guile> `(1 ,@())
Backtrace:
In standard input:
5: 0* (quasiquote (1 (unquote-splicing ())))
standard input:5:1: In procedure memoization in expression (quasiquote (1 #)):
standard input:5:1: Illegal empty combination ().
ABORT: (syntax-error)
Racket не ругается и выдает:
Welcome to Racket v6.2.1.
> `(1 ,@())
'(1)
Chicken Scheme:
#;1> `(1 ,@())
Error: illegal non-atomic object: ()
inside expression `(##sys#cons ...)'
Call history:
<syntax> (quasiquote (1 (unquote-splicing ())))
<syntax> (##sys#cons (##core#quote 1) ())
<syntax> (##core#quote 1) <--
Если перевести это на аналогичный код в Clojure, то он выдает ответ, аналогичный Racket'у:
user=> `(1 ~@())
(1)
Кто прав?
Чисто интуитивно я согласен c Guile и Chicken:
unquote-splicing пыдается вычислить последующую форму (и если она вернет список, то оно подставит элементы списка вместо себя).
Форма после unquote-splicing - пустой список без quote: ()
Оно пытается его выполнить и фейлится (что логично и понятно).
Если «починить» этот пример, добавив quote, то все работает:
guile> `(1 ,@'())
(1)
Но я не совсем понимаю как Racket и Clojure выполняют эту штуку и возвращают '(1).
Ладно, Clojure еще можно понять, там () evaluate'иться в () (quote не нужен). Но Racket?
Или это очередной особый случай?