среда, 8 сентября 2010 г.

Безумство вложенного квотирования

Тут с господином swizard'ом мерялись органами, кто лучше в уме раскрывает вложенные квотирования, на которые он жаловался в своём недавнем посте. Без ошибок никто не раскрыл, но надо отдать должное, что он на своих DSL'ях скобоксобак наелся и раскрывает, всё же, лучше :)

Заодно обнаружили, что компиляторы тоже квотирования не очень-то понимают.

Например, LispWorks 6.0.1 такое
(let ((q '(1 2 3)))
  `(a b `(c d ,,@q)))
раскрывает, как
(a b (system::bq-list (quote c) (quote d) 1 2 3))
Непорядочек! Нельзя в readable-форме светить символ из свого специфичного пакета.

Или всеми нами заслуженно любимый SBCL (1.0.42) раскрывает такую форму:
(let ((q '(1 2 3)))
  `(a b `(c d ,',@q)))
вот так:
(A B `(C D 1))
А должен вот так:
(A B `(C D 1 2 3))

И на закуску, как раскрывают 4 компилятора одну форму:
(let ((v '(+ 1 2)))  ``(a b ,,v))

SBCL-1.0.42:
`(A B ,(+ 1 2))

CLISP-2.49:
(LIST 'A 'B (+ 1 2))
ClozureCL-1.5:
(LIST* 'A (LIST* 'B (LIST (+ 1 2))))
LispWorks-6.0.1:
(system::bq-list (quote a) (quote b) (+ 1 2))
Всё это валидные представления одного и того же объекта :) Если их считать ридером любой имплементации CL и сравнить, то будет полное совпадение. Ну, кроме уже отмеченного бага в LW.

Метапрограммирование на Лиспе заводит свою бензопилу и идёт в ваши ночные кошмары.

6 комментариев:

  1. > Непорядочек! Нельзя в readable-форме светить символ из свого специфичного пакета.

    SBCL в данной ситуации поступает точно так же. Только его pretty-printer вместо sb-impl::backq-list ставит `:

    CL-USER> (write (let ((q '(1 2 3)))
    `(a b `(c d ,,@q)))
    :pretty nil)
    (A B (SB-IMPL::BACKQ-LIST (QUOTE C) (QUOTE D) 1 2 3))
    (A B `(C D ,1 ,2 ,3))

    Эта функция идентична list и используется для того, чтобы pretty-printer красиво печатал подобные выражения (о чём и написано в комментариях:
    ;;; Define synonyms for the lisp functions we use, so that by using
    ;;; them, the backquoted material will be recognizable to the
    ;;; pretty-printer.

    ОтветитьУдалить
  2. Да, ты прав. В LW у меня *print-pretty* в nil стоит. Со включенным претти-принтером всё в порядке.

    ОтветитьУдалить
  3. Кстати, и вот этот пример:
    (let ((q '(1 2 3)))
    `(a b `(c d ,',@q)))
    SBCL раскрывает совершенно корректно - косячит в данном случае его pretty-printer, выводя выражение неправильно:

    CL-USER> (write (let ((q '(1 2 3)))
    `(a b `(c d ,',@q)))
    :pretty nil)
    (A B (SB-IMPL::BACKQ-LIST (QUOTE C) (QUOTE D) (QUOTE 1 2 3)))
    (A B `(C D 1))

    ОтветитьУдалить
  4. И даже понятно почему он косячит - получаемое в результате раскрытия выражение некорректно - у quote ведь должен быть только 1 аргумент а не 3.

    ОтветитьУдалить
  5. Ну, про SBCL - это выражение, вообще говоря, не совсем корректно. И таки да, это баг, если так можно сказать, претти-принтера. И если другие реализации раскрывают его отлично от SBCL, то это, вообще говоря, баги у них(ну, или, намеренные "фичи" - почему-то вспомнился костыль в винде, связанный с обходом неупотребления пользователями __stdcall перед функциями винапи).

    По идее, ну, насколько я понимаю, самый "левый" бэкквот аннигилирует с самым "правым" "раскрытием"(запятая, запятая-собака, запятая-точка).
    Т.е. `(a b `(c d ,(quote ,@q))) это как раз (A B `(C D ,(QUOTE 1 2 3))). В этом можно убедиться, поставив, например, VECTOR вместо QUOTE.

    ОтветитьУдалить
  6. Не совсем корректно сказал.
    Правильнее так:
    Самое левое раскрытие открывает самый внутренний бэкквот, но начинается раскрытие с внешних квазиквотаций.
    Т.е.
    `(a b `(c d (vector ,@q))) это (A B `(C D (vector ,@Q)))

    ОтветитьУдалить

Архив блога