Как следует из документации , syntax-rules
расширяется до syntax-case
.И действительно, syntax-case
имеет ту же проблему:
#lang racket
(define-syntax (minus stx)
(syntax-case stx (from)
[(_ e1 from e2) #'(- e2 e1)]))
(let ([from 1])
(minus 1 from 2))
;; => minus: bad syntax in: (minus 1 from 2)
Однако есть вариант syntax-case
с именем syntax-case*
.В документации говорится, что:
Как и в случае с синтаксисом, но id-compare-expr должен создать процедуру, которая принимает два аргумента.Литеральный идентификатор в шаблоне совпадает с идентификатором, для которого процедура возвращает истину, если ему присвоен соответствующий идентификатор (в качестве первого аргумента), и идентификатором в шаблоне (в качестве второго аргумента).
Другими словами, синтаксис-регистр похож на синтаксис-регистр * с идентификатором сравнения-сравнения, который создает free-identifier =?.
Так что мы можем попробовать это:
#lang racket
(define-for-syntax (my-comparator a b)
(println a)
(println b)
(println (free-identifier=? a b))
(free-identifier=? a b))
(define-syntax (minus stx)
(syntax-case* stx (from) my-comparator
[(_ e1 from e2) #'(- e2 e1)]))
(minus 1 from 2)
;; => .#<syntax:unsaved-editor:13:9 from>
;; => .#<syntax:unsaved-editor:11:11 from>
;; => #t
(let ([from 1])
(minus 1 from 2))
;; => .#<syntax:unsaved-editor:19:11 from>
;; => .#<syntax:unsaved-editor:11:11 from>
;; => #f
;; => minus: bad syntax in: (minus 1 from 2)
КакВы видите, проблема в том, что free-identifier=?
обнаруживает, что оба from
не равны в последнем случае.
Итак, чтобы сделать эту работу, просто предоставьте свой собственный компаратор, которыйне использует free-identifier=?
:
#lang racket
(define-for-syntax (my-comparator a b)
(eq? (syntax->datum a) (syntax->datum b)))
(define-syntax (minus stx)
(syntax-case* stx (from) my-comparator
[(_ e1 from e2) #'(- e2 e1)]))
(minus 1 from 2)
;; => 1
(let ([from 1])
(minus 1 from 2))
;; => 1
Если вы не можете использовать syntax-case*
, вы также можете сделать это:
(define-syntax (minus stx)
(syntax-case stx ()
[(_ e1 from e2)
(eq? (syntax->datum #'from) 'from) ; a guard
#'(- e2 e1)]))
, хотя это становится утомительнымкогда у вас есть несколько буквальных идентификаторов.