I am trying to make a small shell for doing sql-like queries on csv files (out of curiosity and as an attempt to learn Racket). For this I wanted to implement a select macro with this rough structure (where I planned to have x be the columns of the db but just passed a single row for now):
(define-syntax select
(syntax-rules (* from where)
((_ col1 ... from db where condition)
(filter (lambda (x) condition) <minutiae>))))
(Where minutiae is file IO and pipe code)
The scope for x is not what I thought it to be:
x: undefined;
cannot reference an identifier before its definition
I found this example of a let-like macro:
(define-syntax my-let*
(syntax-rules ()
((_ ((binding expression) ...) body ...)
(let ()
(define binding expression) ...
body ...))))
And then I proceeded to try to just produce the lambda like so:
(define-syntax my-lambda
(syntax-rules ()
((_ body)
(lambda (x)
body))))
And then trying to mimic the structure of the let example:
(define-syntax my-lambda
(syntax-rules ()
((_ body)
(lambda (x_)
(let ()
(define x x_)
body)))))
Both of these gave me the same error upon invocation ((my-lambda (+ x 1)) 0):
x: undefined;
cannot reference an identifier before its definition
According to my reading this is due to hygiene, but I can't seem to grasp it well enough to solve this on my own. What am I doing wrong and how would one define these kinds of macros? Why is the let example working but not the lambda one?
Like you guessed, the issue is about hygiene.
The let example works because the identifiers used inside the given body are passed along to the macro.
But If you try to define an
xidentifier inside the body, without having the person writing the body actually know explicitely about it, then you are breaking hygiene (inserting an arbitrary binding inside the scope).What you want to create is called an anaphoric macro. Fortunately, Racket has what you need to make one.
The syntax-parameter
If you ever used Racket parameters before, it work a bit the same, but for macros.
This will define a parameter called
<x>that your macro users will be able to use inside yourselectmacro. To prevent it from being used outside, by default, the parameter is configured to raise a syntax error.To define the only place in which it can be used, you call
syntax-parameterize:This will create a new scope around
conditionin which<x>is bound to thexin lambda.You can then call your macro like so:
If you try using
<x>outside of your macro, you'll get a syntax error:Full code