Is there any way to detect if a macro is expanding inside a pattern match?
Here's an example macro that I'd like to write, but it fails inside a match-define
:
#lang racket/base
(require racket/match (for-syntax racket/base syntax/parse))
(struct point (x y))
(define-syntax (friendly-point stx)
(syntax-parse stx
[(_ arg* ...)
#'(begin (printf "Now making a point\n") (point arg* ...))]
[_ #'(begin (printf "Hello point\n") point)]))
(define p (friendly-point 1 2))
;; Prints "Now making a point"
(match-define (friendly-point x y) p)
;; ERROR
Yes. Instead of using an ordinary syntax transformer created with define-syntax
, use define-match-expander
to create a macro that can cooperate with match
.
(require (for-syntax syntax/parse))
(define-match-expander positive
(syntax-parser
[(_ n)
#'(? positive? n)]))
(match 3
[(positive n) (~a n " is positive")])
; => "3 is positive"
The define-match-expander
form is flexible: it can be used to create macros that may only be used inside of match
, but it can also be used to create macros that expand differently depending on how they are used by providing two transformer functions, one for each context. This allows you to have “context-sensitive” identifiers which work as both functions and as match expanders.
(require (for-syntax syntax/parse)
(prefix-in base: racket/base))
(define-match-expander syntax
(syntax-parser
[(_ x)
#'(? syntax? (app syntax->datum x))])
(make-rename-transformer #'base:syntax))
(match (syntax (1 2 3))
[(syntax n) (~a n " is a syntax list")])
If you need even more flexibility, you can forego define-match-expander
entirely and define a custom struct with the prop:match-expander
structure type property. This can be combined with prop:procedure
to achieve the two-argument functionality described above, but it can also hold state, and it can be paired with other structure type properties such as prop:rename-transformer
to allow the same identifier to function in many, many different contexts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With