Макросы запускаются во время компиляции перед выполнением программы. Во время компиляции вы не можете знать, какое значение будет иметь выражение - информация просто не существует. (Вы можете теоретически проверить такую вещь в языке со статической системой типов, но #lang racket
динамически типизируется.)
Одна вещь, которую вы можете сделать, это заключить контракт в выражение, чтобы он вызывал ошибку времени выполнения, если контракт не совпадает. Синтаксический класс expr/c
предоставляется для этой цели. Вы используете это так:
(begin-for-syntax
(define-syntax-class model-property-guard
#:description "a property guard"
(pattern (~var guard (expr/c #'procedure?))
#:with c #'guard.c)))
(define-syntax (m stx)
(syntax-parse stx
[(_ guard:model-property-guard)
#'guard.c]))
Используя приведенные выше определения, запись (m add1)
будет успешно производить #<procedure:add1>
, а запись (m 1)
завершится с ошибкой во время выполнения с нарушением контракта:
m: contract violation
expected: procedure?
given: 1
in: procedure?
Обратите внимание, что расширение должно использовать guard.c
в расширении! Атрибут c
содержит модифицированное выражение, которое присоединяет контракт к значению, а прямое использование guard
просто передает выражение без изменений, без присоединенного контракта.
Дополнительные примеры expr/c
в действии см. В Контракты по подвыражениям макросов .