我正试图定义与过程参数相匹配的语法类。
我知道如何匹配标识符,表达式和其他语法类。
这是我的示例:
(define-syntax-class model-property
#:description "a model property"
#:attributes (name datatype guard)
(pattern name:id
#:with datatype #`null
#:with guard #'(lambda (value) value)
)
(pattern [name:id #:datatype [datatype:id #:not-null] #:guard guard:expr])
)
我想将#:guard guard:expr
替换为#:guard guard:procedure
我尝试过
(define-syntax-class model-property-guard
#:description "a property guard"
(pattern guard:expr
#:fail-when (procedure? #'guard)
"property guard should be procedure."))
有可能吗?怎么样?
答案 0 :(得分:4)
宏在程序执行之前在编译时运行。在编译时,您不知道表达式将产生什么样的价值-信息根本不存在。 (理论上,您可以使用具有静态类型系统的语言来检查这种情况,但是#lang racket
是动态键入的。)
您可以 要做的一件事是在表达式上放置合同,以便在合同不匹配时引发运行时错误。为此提供了expr/c
syntax class。您可以这样使用它:
(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
的更多示例,请参见Contracts on Macro Sub-expressions。