@pau.cervera has joined the channel
@laurent.orseau has joined the channel
One problem with syntax-parse is that it does a lot, which looks magical at times and confusing for the beginner. syntax-case also does a lot, but less. I still recommend syntax-parse though, but maybe starting with define-simple-macro.
agreed, using define-simple-macro
whenever possible is good
(especially because it automatically adds #:track-literals
)
@jestarray has joined the channel
@sorawee @notjack thank you for the fix, i thought the syntax class there was just to suggest what class should the syntax be recognized as… not that it needed to be that syntax class.
I’ve been working with syntax/parse, but am having trouble controlling the macro expansion search.
Here’s an example to illustrate:
(define-syntax-parser bar [(_ x) #'+])
(define-syntax-parser foo [(_ x) #'(bar x)])
A call to (foo 'a)
is expanded into (bar 'a)
, which is expanded into #<procedure:+>
. I want (foo 'a)
to instead expand into '+
. How can I achieve this without modifying the internals of bar
?
A naive attempt A naive attempt would be to quote the call to (bar x)
:
(define-syntax-parser foo [(_ x) #'(quote (bar x))])
This does not work—it expands to the quoted expression (bar x)
. This is understandable because the quote
form does not expand it’s internals.
Action patterns I suspect that action patterns can be used to solve this. <https://docs.racket-lang.org/syntax/stxparse-patterns.html?q=pattern#%28form._%28%28lib.syntax%2Fparse..rkt%29.~7ebind%29%29|According to the syntax/parse docs>, the ~bind action pattern evaluates it’s expressions and binds them to results. But this still expands to (bar x)
:
(define-syntax-parser foo
[(_ x (~bind [result #'(bar x)]))
#'(quote result)])
I could be misusing the pattern, or misunderstanding what is meant by “evaluation” here.
How can I quote the expansion of (bar x)
?
[meta] I’m wondering if we shouldn’t avoid having questions about macros on the #beginners channel—apart maybe about real basics of macros. I worry this may confuse and scare actual beginners.
My apologies—I’ve only been working with racket proper for a few weeks, and can’t gauge the level of difficulty of this question. Please feel free to point me to a better channel. I can remove the message if it causes concern.
#lang racket
(require syntax/parse/define)
(define-syntax-parser bar [(_ x) #'+])
(define-syntax-parser foo
[(_ x)
#:with expanded (local-expand #'(bar x) 'expression #f)
#''expanded])
(foo 'a)
Use local-expand
to force the macro expander to expand a syntax object right away. Then, once you have #'+
in your hand, you can quote it.
No worries at all, my question was not aimed at you, but at those who’ve been her for a while. You can ask this story of questions on #general i suppose (where you may even get better help for these), but again I was wondering more than asserting so it’s all fine :)
Thank you very much! I think this means that ~bind
doesn’t expand it’s expressions. If so, what is meant by “evaluate” in the racket docs?
(~bind [attr-arity-decl expr] ...)
Evaluates the exprs and binds them to the given attr-ids as attributes.
Thank you, that’s good to know
I posted my own macro question to both #beginners and #general, not being sure which was more appropriate. I was actually more worried that my question might be judged too beginner-y for #general, but I guess it’s the other way around.
“Expansion” and “evaluation” are different. When you write #'(bar x)
, you are constructing a syntax object that is a syntax list, with bar
as a first element and x
as the second element. It really doesn’t compute anything further because a syntax object is a value. It evaluates to itself. But once you return this syntax object to the macro expander, the macro expander then can expand the syntax object further.
Another way to do things is to write your bar
as a procedure at compile-time. Like this:
(define-for-syntax (bar x)
#'+)
In this case, bar
is just a regular procedure (but defined at compile-time), so in foo
, you can do something like:
(define-syntax-parser foo
[(_ x)
#:with evaled (bar #'x)
#''evaled])
In the above code, (bar #'x)
does computation. It evaluates to #'+
.
~bind
is a little bit more complicated. One instance where it’s useful is when you are conditionally pattern matching a syntax object (via ~or*
or ~optional
) and in one branch, you want to conditionally bind a variable to a result of a computation. But it doesn’t expand anything.
We’ve also gotten some Typed Racket questions that seem a bit beyond beginner (at least to me.)
I have been thinking the same.
i had the same worry. i actually just posted a question in beginners about macros, though it was more of an specific behavior on syntax-parse. even though, i already posted two questions in general… it’s just upon knowing there was a #beginners channel i just thought that i shouldn’t bother #general …
Over in the F# Foundation’s Slack there is a rule (not strictly enforced) that the beginner’s channel is “A monad-free zone”, since questions about that sort of thing are a bit more advanced and have the potential to scare off beginners with F#.