Question 1 @ 2025-04-08 19:23
Why are symbols produced by (gensym)
guaranteed to be unique?
-
(gensym)
checks all symbols in the program and produces a “fresh” one in the sense that it is not used in it. -
(gensym)
increments a number which is part of the produced symbol every time it is called. -
(gensym)
produces uninterned symbols, which ensures they are noteq?
to any other symbol (regular one, or a result of another(gensym)
). -
They are not guaranteed to be unique:
(gensym)
produces a highly random symbol name using a cryptographically-robust random number (effectively a sha256 random number) so chances of name collisions are negligibly small. -
The produced symbol is based on machine+user identification, thereby producing a symbol which is, like a UUID, completely unique.
Question 2 @ 2025-04-08 19:25
Say that we run the following code in #lang racket
with the
define-macro
tool that we’ve seen:
(list 'if <expr1> <expr1> <expr2>))
(define (inc! x) (set-box! x (add1 (unbox x))) (unbox x))
(define a (box 0))
(define b a)
(orelse (inc! a) (inc! b))
What would the result be, and is it showing that the macro is implemented properly?
-
The proper result is 1.
-
The improper result is 1 (incorrect because both of the
orelse
subforms are evaluated). -
The proper result is 2.
-
The improper result is 2 (incorrect because the first subform is evaluated twice).
-
The proper result is 3.
-
The improper result is 3 (incorrect because the first subform is evaluated twice, and the second once).
-
The result is a void value (so, no output at all), since all of the forms, including the last one, are used for their side-effects.
-
The result would be incorrect regardless of macro details, because we have a case of identity “aliasing” where two values are the same exact box object.
Question 3 @ 2025-04-08 19:31
Focusing on the macro from the last question:
(list 'if <expr1> <expr1> <expr2>))
How could we solve the problem of this macro?
(Important: choose the best answer!)
-
There is no problem with it, so no solution is needed.
-
The problem was with box identity (more precisely, with aliasing + mutation), so changing the macro cannot improve this case.
-
Use
define-syntax
+syntax-rules
. -
Like (C), but better: use
define-syntax-rule
. -
Use a temporary
gensym
-ed binding. -
Like (E), but better: also use quasiquotes and unquotes to make it look more like a code template.
Question 4 @ 2025-04-08 19:32
What is the biggest advantage of define-macro
over define-syntax
+
syntax-rules
?
(Important: choose the best answer!)
-
It is more hygienic.
-
It is less hygienic.
-
It avoids name captures.
-
It can “inject” known identifiers into macro results
-
It is using the host language (Racket) for the macro definition, which means that you can do arbitrary computations at the macro level.
-
It can be used to implement a whole compiler instead of just macros.
Question 5 @ 2025-04-08 19:34
Your friend Random J. Hacker heard about macros and wants to use one to create a useful debugging tool:
(let ([result expr])
???
result))
The idea is to print the expression and its result in the ???
line.
Could this be achieved, and how?
-
There is no way to get it done, because it will inevitably lead to evaluating the expression twice.
-
It’s easily done, eg:
(printf "result: ~s\n" result)
, but there’s not much point in doing that as a macro since the same could be done as a simple function too. -
We could try to do it like so:
(printf "~s => ~s\n" expr result)
but as we clearly see, this will lead to a double evaluation of the input expression. -
We can use
(printf "~s => ~s\n" 'expr result)
to print the actual (quoted!) expression as well as what it evaluated to. -
We can use
(printf "~s => ~s\n" 'expr 'result)
to print the actual expression as well as what it evaluated to, using quotes to avoid undesired evaluations.