Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

racket: macro expand inside match pattern

Tags:

macros

racket

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
like image 395
Ben Greenman Avatar asked Dec 19 '22 19:12

Ben Greenman


1 Answers

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.

like image 144
Alexis King Avatar answered Feb 24 '23 05:02

Alexis King