Существует два класса стратегий для исправления ошибок «значение атрибута ложно»:
Укажите значения по умолчанию или альтернативы, чтобы атрибут никогда не был ложным.
(a) Использование ~optional
с #:defaults
(b) Использование ~or
, возможно, с ~parse
(c) Использование синтаксического класса с несколькими шаблонами
Обработка атрибута, являющегося ложным в теле.
(a) Использование unsyntax
и if
(б) Использование ~?
1 (а)
Реализации 2 и 3 в вопросе, а также код в ответе Жерома, все используют ~optional
с #:defaults
, поэтому все они пытаются исправить это с помощью 1 (a).
Ответ Жерома показывает правильное использование этого. Суть в том, что если вы используете атрибут типа <object>.result
, вам нужно указать значение по умолчанию для <object>.result
, а не просто <object>
.
Однако есть один недостаток, если в классе obj-exp
есть несколько атрибутов, которые вам нужно использовать. Для этого потребуется указать значение по умолчанию для каждого:
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object>.result 1) null]
[(<object>.x 1) null]))
1 (б)
Если вы используете несколько атрибутов из синтаксического класса, таких как <object>.result
, <object>.x
, <object>.y
, <object>.z
и т. Д., То 1 (a) потребует от вас указать значение по умолчанию для каждого из них в отдельности. Чтобы избежать этого, вместо того, чтобы писать это:
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object>.result 1) null]
[(<object>.x 1) null]
[(<object>.y 1) null]
[(<object>.z 1) null]))
Вы можете использовать ~or
и ~parse
следующим образом:
(~or (objects <object>:obj-exp ...)
(~and (~seq) (~parse (<object>:obj-exp ...) null)))
1 (с)
(define-splicing-syntax-class maybe-objects
#:datum-literals (objects)
[pattern (objects <object>:obj-exp ...)]
[pattern (~seq) #:with (<object>:obj-exp ...) null])
2 (а) и (б)
Реализация 4 в вопросе использует unsyntax-splicing
и if
, так что это пример 2 (a):
#,@(if (attribute <object>)
#'(<object>.result ...)
#'())
Однако, как вы заметили, это выглядит некрасиво. И это также имеет другую проблему. Если это само было под многоточием, это ломается, потому что эллипсы не несут свои эффекты внутри #,
или #,@
.
Именно поэтому ~?
существует для 2 (b). Вместо использования #,@(if ....)
вы можете использовать:
(~? (<object>.result ...) ())
Это не совсем работает, но этот вариант работает:
(~? (list <object>.result ...) (list))
Использование этого в варианте реализации 4:
(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)))
#`(bag <label>
(~? (list <object>.result ...) (list)))]))