Jaro
Jaro

Reputation: 3887

racket - define-syntax-class with pattern matching procedure

I'm tring to define syntax class matching a parameter which is procedure.

I know how to match identifiers, expressions, another syntax classes.

This is my sample:

(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])
    )

And I'd like to replace #:guard guard:expr with something like #:guard guard:procedure

I experimented with

(define-syntax-class model-property-guard
 #:description "a property guard"
(pattern guard:expr
         #:fail-when (procedure? #'guard)
         "property guard should be procedure."))

Is it possible? How?

Upvotes: 3

Views: 256

Answers (1)

Alexis King
Alexis King

Reputation: 43902

Macros run at compile-time, before the program is executed. You can’t, at compile-time, know what sort of value an expression will produce—the information simply doesn’t exist. (You could theoretically check such a thing in a language with a static type system, but #lang racket is dynamically typed.)

One thing you can do is put a contract on an expression so that it raises a runtime error if the contract isn’t matched. The expr/c syntax class is provided for this purpose. You use it like this:

(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]))

Using the above definitions, writing (m add1) will successfully produce #<procedure:add1>, while writing (m 1) will fail at runtime with a contract violation:

m: contract violation
  expected: procedure?
  given: 1
  in: procedure?

Note that the expansion must use guard.c in the expansion! The c attribute contains a modified expression that attaches a contract to the value, and using guard directly would merely pass the expression through unchanged, without the contract attached.

For some more examples of expr/c in action, see Contracts on Macro Sub-expressions.

Upvotes: 5

Related Questions