
Question. Is it possible to create a syntax object via a literal that contains multiple consecutive expressions of syntax? E.g. could I write this macro without the added (begin)
form? (define-syntax-rule (require/provide path)
(begin
(require path)
(provide (all-from-out path))))
As soon as I move to define-syntax
with syntax-parse
, I don’t know how to write the syntax literal anymore, since it is multiple expressions: (define-syntax (require/provide stx)
(syntax-parse stx
[(_ path)
#'#((require path)
(provide (all-from-out path)))]))

is the #'#
construct the correct way to wrap multiple lines of syntax into a single syntax object?

it seems to work, but i dont understand it :confused:

There’s no #'#
operator.

I think you simply want:
(define-syntax (require/provide stx)
(syntax-parse stx
[(_ path)
#'(begin (require path) (provide (all-from-out path)))]))

This is not directly related to syntax-parse
btw. If you use syntax-case
or produce a syntax object directly, you would need to write #'
too.

define-syntax-rule
is a shorthand that simplifies this step by removing the need to write #'
.

yeah i understand that part, my question is about syntax objects: > Is it possible to create a syntax object that contains multiple lines of syntax? (without the use of (begin)
)

because syntax objects seem to be able to hold multiple lines

Sorry, I’m a bit confused. Why don’t you want to use begin
?

oh don’t get me wrong, i have nothing against begin
, I’m just trying to understand syntax objects

e.g. if I run syntax-e
I can get a syntax object back which contains multiple lines of code

so i don’t understand why i wouldn’t be able to create such an object manually

Ah, yes, but the macro expander will not treat that as many lines of code

It will treat that as a single line of code in parentheses (or #(...)
, or whatever you use to group them)

For #(...)
, that creates a vector.

For (...)
, that results in either a macro invocation or function application.

So using begin
is essentially the only possibility of “grouping” multiple lines together

hm, ok, interesting

so what does actually get created when I use this kind of code? #'#((require path)
(provide (all-from-out path)))

I would not use the term “line” btw. Let’s say “expressions” or something like that instead

is that a syntax object with a vector inside?

That creates a vector.

Yes

ah, makes sense

#lang racket
(require (for-syntax syntax/parse))
(define-syntax (require/provide stx)
(syntax-parse stx
[(_ path)
#'#((require path)
(provide (all-from-out path)))]))
(require/provide 123)

results in:
'#((require 123) (provide (all-from-out 123)))

which is a vector containing two lists: the list with require
as the first element and the list with provide
as the first element.

ok, i understand it now :+1::skin-tone–2:

and if i were to dig deeper in macro expansion, i would find that even things like function bodies secretly use begin
as the only to way to contain multiple expressions in a syntax object?

So, actually, let me correct one thing.

It’s OK to use ()
, or even #()
as a way to group stuff during the macro expansion. But the final expansion should not use ()
or #()
for grouping

For example:

#lang racket
(require (for-syntax syntax/parse))
(define-syntax (reverse/helper stx)
(syntax-parse stx
[(_ () (acc ...))
#'(acc ...)]
[(_ (hd tl ...) (acc ...))
#'(reverse/helper (tl ...) (hd acc ...))]))
(define-syntax-rule (reverse xs ...)
(reverse/helper (xs ...) ()))
(reverse 3 5 -)

This is a silly macro that transforms (reverse 3 5 -)
to (- 5 3)
.

Here, I use (......)
as a way to group multiple expressions together in the intermediate steps in the expansion. But the final result doesn’t use (.....)
for grouping. It just produce one single term.

That is, the expansion roughly looks like this

(reverse 3 5 -) =>
(reverse/helper (3 5 -) ()) =>
(reverse/helper (5 -) (3)) =>
(reverse/helper (-) (5 3)) =>
(reverse/helper () (- 5 3)) =>
(- 5 3)

yeah and to “aid” the wrapping of multiple expressions in a single form you have a recursive call now

but begin
would also be able to handle that case

ok

thanks a lot :slightly_smiling_face:

insightful things

@bran.van.der.meer FWIW I mention the technique of wrapping begin
around definitions and expressions in the first example. https://soegaard.github.io/mythical-macros/

@soegaard2 FYI: in the recent Racket versions, the term “tamper status” is removed from the doc. The concept is simplified so that an object is either tainted or not tainted. You might want to update the tutorial.
Also, the title is named “titel”, which is probably a typo.

Also: wrong closing single quote

@sorawee Thanks for the tips. I’ll remove “tamper status”.

I have no clue why the closing quote is the wrong way around. I must have a bug somewhere.

Another thing: after you introduce syntax-parse
, you might want to teach the #:with
feature, which allows users express with-syntax
/ with-syntax*
flatly.

the recipe can even switch to use #:with
.

The idea was to introduce with-syntax
as a primitive (it’s useful to know about it for syntax-case
macros). The intention was then to introduce #:with
later on - but that section has yet to be written. When it materializes, I’ll add a note in the with-syntax
section that #:with
can be used.

Fun fact - the use of single quotation marks in different countries (it’s a mess).

Yikes, that should be a new chapter in the “Falsehoods Programmers Believe About ___” series.

Where previous installments are “Names”, “Addresses”, etc.