Макросы и внутренние определения в схеме
Хороший вопрос был задан на #scheme канале Freenode. Рассмотрим следующий код на схеме:
(define alpha 1)
(define-syntax foo
(syntax-rules (quote alpha)
((_ alpha msg) (define bar 2))
((_ other msg) (syntax-error msg)) ) )
(define (beta)
(foo alpha "beta")
(define alpha 3)
'beta )
(define (gamma)
(define alpha 4)
(foo alpha "gamma")
'gamma )
(define (delta alpha)
(foo alpha "delta")
'delta )
Какие из beta
, gamma
, а также delta
должен производить синтаксические ошибки? А что делать? Я проверил это с помощью схемы Чиби, где beta
хорошо в то время как gamma
а также delta
потерпеть поражение. Интересно, это намеченное поведение или просто ошибка в Chibi.
Согласно стандарту, кажется, что расширение макросов должно произойти до того, как внутренние определения будут переписаны в letrec*
, Так beta
а также gamma
оба должны потерпеть неудачу как foo
будет соответствовать с внутренне определенным alpha
не глобальный.
Однако в стандарте явно не указано, как на самом деле работают внутренние определения, только то, что их можно рассматривать как ярлык letrec. Я получаю такое же поведение с Racket R5RS, поэтому мне кажется, что в стандарте отсутствует что-то, что требует такого поведения.
3 ответа
Хорошо, я наконец понял ваш вопрос. Выполнение вашего кода было сложным, потому что у вас, кажется, есть функция "синтаксическая ошибка", которая сигнализирует о синтаксической ошибке, только если она заканчивается в полностью расширенном коде. Без разницы.
Я думаю, что ответ на ваш вопрос таков:
Эти парни из Схемы (Дибвиг, Феллайзен, Хиб, Клингер, Рис, Ванд, Флатт, Калпеппер и т. Д.) Довольно умны!
В частности, каким-то образом Scheme/Racket удается выяснить, как работает структура привязки, даже когда она не знает, будет или нет привязка. Ты прав! Это безумие! Но алгоритм, изложенный Dybvig et al. делает некоторые очень умные вещи, чтобы гарантировать, что гигиена отслеживает, являются ли идентификаторы "равными свободным идентификаторам" или "равными связанным идентификаторам" (терминология Флатта), даже когда он еще не знает, какой из них связывает другой. Я лично рекомендую прочитать "Макросы, которые работают вместе" (Флатт, Калпеппер, Дарэ, Финдлер) для лучшего понимания этого.
Извиняюсь, если я неправильно понял ваш вопрос, или если мой тон неуместен!
Может быть слишком много в зависимости от реализации, но это происходит из-за порядка раскрытия макроса. Теоретически все определения содержат alpha
поэтому он не должен совпадать с тем, что в буквальных ключевых словах. Тем не менее, расширение макросов должно быть сделано до define
формы расширены до letrec*
иначе компилятор не сможет правильно определить внутреннее определение. Поэтому в этот момент компилятор может видеть или не видеть привязки. (Время раскрытия макроса не указано в R7RS, поэтому реализация может также выбрать собственную синхронизацию.)
Для beta
в этом случае компилятор не перехватил привязку, поэтому макроэкспандер все еще может видеть это alpha
такая же привязка, как глобальная. В других случаях все наоборот.
Прежде всего, delta
отсутствует (не должно совпадать alpha
) потому что это явно лексически связывает alpha
к другому обязательному, чем тот, под которым ваш sytnax-rules
появляется. Интересные из них beta
а также gamma
,
Согласно разделу 5.2.2. R4RS (стр. 13) и R5RS (стр. 16), раздел 5.3.2. R7RS (стр. 26) и раздел 11.3. R6RS (стр. 32), "область" привязки, установленная посредством внутреннего определения, является полной <body>
в котором появляется определение. И ваш макрос вызов foo
явно в пределах того же <body>
как эти внутренние определения.
R7RS также идет немного дальше и предупреждает нас:
Обратите внимание, что такое тело [то есть то, которое содержит внутренние определения], может быть не очевидно до тех пор, пока не будет расширен другой синтаксис.
Таким образом, запутанный порядок событий допускается, но нет никакой двусмысленности; ваш syntax-rules
не должен соответствовать alpha
ветвь, если есть привязка для alpha
по любому внутреннему определению в том же <body>
как вызов макроса. Следовательно, beta
а также gamma
также отсутствуют.
Приложение
Если мы усложним ситуацию дальше, а сам твой макрос условно связан alpha
, лайк
(syntax-rules (alpha)
((_ alpha x) (define alpha x)))
на первый взгляд это кажется действительно двусмысленным, но я полагаю, что это решается тем фактом, что макроэкспандер переименует определенный alpha
идентификатор в соответствии с гигиеной, что означает, что мы не будем скрывать alpha
который мы сопоставляем как литерал, так что сопоставление это нормально, и приведенное выше просто создаст привязку для переименованного alpha
это недоступно вне тела макроса.
Приложение Б
В конце раздела 5.3 есть ограничение. R5RS (стр. 17), конец раздела 5.4. R7RS (стр. 26) и в середине раздела 10. в R6RS (стр. 30), в которых упоминается, что последовательность определений не должна содержать определения, которое меняет значение любого из них. (На самом деле все немного сложнее, все три стандарта используют разные формулировки, но это должно быть разумное резюме.)
В твоем примере мне не понятно, есть ли у тебя возможность syntax-rules
расширение до синтаксической ошибки считается двусмысленностью ее "значения". Если кто-то считает это неоднозначным, то ваш beta
а также gamma
"ошибки" (неопределенное поведение) в соответствии с R5RS и R7RS и "нарушение синтаксиса" в соответствии с R6RS.
Если ваш пример содержал другую привязку во второй ветке вашего syntax-rules
(в идеале определение для той же переменной даже), тогда этот задира не подойдет, поэтому ваш вопрос стоит.