Hi all,
In the following example, a syntax error is raised because of (apply op args)
can't work when op
is a macro like and
.
How can I force the macro to go through the second branch when apply
doesn't apply?
#lang racket
(require (for-syntax racket/base
syntax/parse))
(define-syntax (thingy stx)
(syntax-parse stx
[(_ (op:expr arg:expr ...))
#'(let ([vals (list arg ...)])
(list (apply op vals) ; can't apply `and` here
(map cons (list 'arg ...) vals)))
]
[(_ ex:expr)
#'ex]))
(thingy (+ 3 4)) ; fine
(thingy (and #true #false)) ; fails with `and: bad syntax`
sorawee
November 15, 2024, 1:23pm
2
It’s possible to create something like my-and
which is still a macro, but can be used as an identifier macro (and therefore would work with apply
). I guess you want stuff like my-and
to go through the first branch?
Here’s one possible implementation.
#lang racket
(require syntax/parse/define)
(define-syntax-parser my-and
[(_ x ...) #'(and x ...)]
[_:id #'(λ args (andmap values args))])
'my-and-works
(my-and #f #t)
(map my-and '(#t #t #f #f) '(#t #f #t #f))
(define-for-syntax (id-use? t)
(define proc (syntax-local-value t (λ () #f)))
(cond
[proc
(with-handlers ([exn:fail? (λ (_) #f)])
(proc t)
#t)]
[else #t]))
(define-syntax (thingy stx)
(syntax-parse stx
[(_ (op:expr arg:expr ...))
#:when (id-use? #'op)
#'(let ([vals (list arg ...)])
(list (apply op vals)
(map cons (list 'arg ...) vals)))
]
[(_ ex:expr)
#'ex]))
'thingy-works
(thingy (+ 3 4)) ; fine
(thingy (and #true #false))
(thingy (my-and #true #false))
EmEf
November 15, 2024, 1:30pm
3
#lang racket
(require (for-syntax racket/base
syntax/parse))
(define-syntax (thingy stx)
(syntax-parse stx
[(_ (op:expr arg:expr ...))
#:with (val ...) (generate-temporaries #'(arg ...))
#'(let ([val arg] ...)
(list (op val ...) ; can't apply `and` here
(map cons (list 'arg ...) (list val ...))))
]
[(_ ex:expr)
#'ex]))
(thingy (+ 3 4)) ; fine
(thingy (and #true #false)) ; fails with `and: bad syntax`
Thanks Sorawee!
I'm not sure I want something like my-and
actually, because some branches of the and
may not be meant to be evaluated.
However, something like id-use?
is what I'm looking for. Though I'm hoping to not have to use with-handlers
if possible.
Thanks Matthias!
Unfortunately, I don't think I'm allowed to evaluate the arguments of the and
beforehand, because it defies the evaluation order that and
creates.
For example, (thingy (or (not (vector? v)) (vector-ref v 0)))
will fail if v
is not a vector, but shouldn't.
So I think I need to make the macro fall back to the second branch instead.
EmEf
November 15, 2024, 2:07pm
6
The evaluation order of my revision is the same as yours (if yours worked).
Absolutely, but mine doesn't work either!
That's why I want to push cases where I can't apply
to the second branch —
but I guess the "Can I use apply
?" is a proxy for "is it safe to evaluate the inner expressions before the outer one?".
Your revision helped me realize this more clearly.