I've just started diving into Racket macros, and am trying to make a terse simple-macro-defining macro. I would like to expand an expression like this:
(macro id
(param) replacement1
(params ...) replacement2)
Into something like this:
(define-syntax id
(syntax-rules ()
((id param) replacement1)
((id params ...) replacement2)))
So the cddr of the original expression is turned into pairs of expressions (for use in the syntax-rules body), and the id is inserted into the car of each of these pairs.
I'm having trouble thinking recursively when using only the pattern-matching provided by syntax-rules (I keep wanting to manipulate the expression as though it were a normal list). What kind of pattern should I use? Or, can I somehow manipulate it as a normal list, and then unquote the result for use in the expansion?
Many thanks
Edit - tentative solution, informed by Taymon's answer
Part of my curiosity here was about getting rid of those pairing parentheses. I looked into syntax-case, but got a bit confused, so tried to do it purely with the pattern-matching sub-language. I ended up using Taymon's macro combined with another macro to 'pairize' the given templates (it acts kind of like an accumulator function):
(define-syntax-rule (macro-aux id ((param ...) expr) ...)
(define-syntax id
(syntax-rules ()
((id param ...) expr)
...)))
(define-syntax pairize
(syntax-rules ()
((pairize id (pairs ...) p b) (macro-aux id pairs ... (p b)))
((pairize id (pairs ...) p b rest ...) (pairize id (pairs ... (p b)) rest ...))))
(define-syntax macro
(syntax-rules ()
((macro id tpl-expr ...) (pairize id () tpl-expr ...))))
It is possible to build a macro expander that manipulates the syntax expression as regular Racket data. However, that's not really necessary in this case.
One thing I would recommend is changing your syntax slightly, so that each pattern-replacement pair is enclosed in brackets. Like this:
(macro id
[(param) replacement1]
[(params ...) replacement2])
Once that's done, you can just use a regular pattern-matching macro. Here's my take on it:
(define-syntax-rule (macro id [(param ...) replacement] ...)
(define-syntax id
(syntax-rules ()
[(id param ...) replacement] ...)))
Taymon is right, but it is also possible to do it with ellipses without wrapping the pattern-replacement pairs in brackets, using ~seq from syntax/parse:
(require syntax/parse/define)
(define-simple-macro (macro id (~seq (param ...) replacement) ...)
(define-syntax id
(syntax-rules ()
[(id param ...) replacement] ...)))
Which can be used like you originally wanted:
(macro id
(param) replacement1
(params ...) replacement2)
Related
I have a macro that extends the racket syntax, and at some point accepts a sequence of bog standard racket expressions. This looks something like this, the relevant syntax variable being body:
(syntax-parse stx
[(_ some-id:id
body:expr ...+)
This macro generates a racket class with a generated method like so:
#'(<class stuff>
(define/public (some-id some-formal-parameter)
body ...)
As I said the body is plain racket code, except for one expression that can be used exclusively in the body, for example:
(define-syntax-rule (tweet identifier value)
(send this publish-tweet (quote identifier) value))
But this does not allow me to use some-formal-parameter because it is not defined. Is there some proper way in which I can define something that can exclusively be used in the body, and can still bind to variables in the context after expansion? Maybe via a splicing syntax class? Reusability is a big bonus, since this "type of body" may exists in multiple (similar) macros.
Some code for testing:
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (define-something stx)
(syntax-parse stx
[(_ some-id:id
body:expr ...+)
#'(define some-id
(new
(class object%
(super-new)
(define/public (displ arg)
(displayln arg))
(define/public (tick some-formal-parameter)
body ...))))]))
(define-syntax-rule (tweet value)
(send this displ value))
(define-something derp
(define a 'not-derp)
(tweet a))
(send derp tick 'derp)
To reformulate the original question now that I know (and can answer) what I wanted to ask: how can I define a macro that can only be used in a certain context (for me: in the body of a method of a racket class), and how can I use dynamically bound variables. In the original code above: when using the expression (tweet a) not only do I want the value of a, but also the value of some-formal-parameter which is bound in the context of the code where the tweet macro is expanded (not where it is defined).
Chris Jester-Young kindly pointed me to syntax parameters, which indeed seem to solve both the issue of dynamic binding and "can only be used in certain contexts". A paper by Eli Barzilay, Ryan Culpepper, and Matthew Flatt helped me understand syntax parameters.
With respect to the original example code I posted, this is the solution I have come up with:
#lang racket
(require
racket/stxparam
(for-syntax syntax/parse))
(define-syntax-parameter tweet
(lambda (stx)
(raise-syntax-error 'tweet "use of an actor keyword outside of the body of an actor" stx)))
(define-syntax (define-something stx)
(syntax-parse stx
[(_ some-id:id
body:expr ...+)
#'(define some-id
(new
(class object%
(super-new)
(define/public (tick some-formal-parameter)
(syntax-parameterize
([tweet
(syntax-rules ()
[(_ value)
(begin (displayln some-formal-parameter)
(displayln value))])])
body ...)
))))]))
(define-something derp
(define a 'not-derp)
(tweet a))
(send derp tick 'derp)
The three key points of attention are the following.
Because of the definition of the tweet macro, whenever it is used outside of the context of a syntax-parametrize statement (that changes the definition of tweet) it will throw an appropriate error.
In the body of the public method tick of our class we change thedefinition of tweet to a macro that matches a pattern of the form (_ value) (which is the value we supply to tweet)
The tweet macro can expand to something that both uses the bound value value, and the value of some-formal-parameter, whatever that may be.
I do not know if this is the proper way to deal with such a situation, but it seems good.
Based on the example provide in the practical common lisp reference, I define a macro to create a class as followed.
(defmacro define-class (class-name class-slot)
`(defclass ,class-name ()
,(mapcar #'slot->defclass-slot class-slot))))
The function slot->declass-slot take a single argument and generate a standard line describing a slot in a class. The code is the following:
(defun slot->defclass-slot (spec)
`(,spec :initarg ,(as-keyword spec) :accessor ,spec :initform 0))
For example,
(slot->defclass-slot 'nom)
(NOM :INITARG :NOM :ACCESSOR NOM :INITFORM 0)
All this work fine, when I create a class 'model' as follow:
(define-class model (nom id))
But suppose that I define a parameter instead.
(defparameter *test* '(nom id))
(define-class model *test*)
Then, the code end-up in an error:
The value *TEST* is not of type LIST.
What is wrong?
Your define-class macro does not evaluate its class-slots argument.
You can "fix" your code like this:
(defmacro define-class (class-name class-slots)
`(eval
`(defclass ,',class-name ()
,#(mapcar #'slot->defclass-slot ,class-slots))))
(macroexpand-1 '(define-class model '(nom id)))
(defparameter *test* '(nom id))
(define-class model *test*)
Note that you now have to quote the literal second argument to define-class.
Note also that you are now using eval (for a good reason, in this case).
Note finally that I seriously doubt that you truly want to do this. Chances are you don't need this level of dynamism, and you are just complicating your life for no good reason.
E.g., if you just want to get the list of class slots (using your *test* variable), you should use MOP instead.
In fact you can make your macro expand to the function ensure-class:
> (mop:ensure-class 'foo :direct-slots '((:name a)))
#<STANDARD-CLASS FOO>
but this relies on a somewhat brazen assumption that your implementation is MOP-compliant.
(defparameter *test* '(nom id))
(define-class model *test*)
You shouldn't try to do this, for the same reason that you never try to do:
(with-open-file '(...)
...)
The point of the macro is to not evaluate the arguments in order that you can do something with them. What you can do instead, if you do for some reason, need both a macro- version and non-macro- version, is to define the macro functionality in terms of a function, and then wrap the function in a macro when you need a macro. E.g., (for a not-particularly robust) with-open-file):
(defun %with-open-file (filename function &rest args)
(let ((file (apply 'open filename args)))
(prog1 (funcall function file)
(close file))))
(defmacro with-open-file ((var filename &rest args) &body body)
`(%with-open-file ,filename
(lambda (,var) ,#body)
,#args))
Then you can use the macro-version when you want it, and the function-version when you want it. In your case, though, that's not a perfect solution, since you're expanding to another macro call.
I want to be able to write a nested expression like this:
(AND/OR expr1 op1 expr2 AND/OR expr3 op2 expr4 and so on)
Where AND/OR is essentially AND or OR. But I want to be able to write an infinite amount of them. I'm using the define-syntax to try to make this happen but I'm not sure how to accept infinite amounts of nested expressions.
Don't mind the expr's and op's in my example, that part I can handle myself. I only want to know how to accept infinite nesting.
Example:
(SELECT somecolumns
FROM sometable
WHERE something
AND/OR something
AND/OR (something AND/OR something)
AND/OR ...)
As Asumu says, in general it's simpler to deal with s-expressions, at least in order to ensure correct operator priority, but for some simple cases pattern matching of syntax-rules (and syntax-parse and co) makes this easy, using rest arguments and recursive matching:
#lang racket
(define-syntax parse-args
(syntax-rules (AND) ; treat AND as a literal
[(_)
; no more argument, return value:
'()]
[(_ (arg1 AND in-rst ...))
; Composed argument found, call parse-args recursively:
(parse-args arg1 AND in-rst ...)]
[(_ arg1 AND rst ...)
; AND operator found, parse left side and rest
(list 'and
; parse the argument (may be composed or not):
(parse-args arg1)
; then parse the rest of the arguments:
(parse-args rst ...))]
[(_ arg)
; in case the argument is not composed or does not contain AND, don't parse it
arg]))
;; TESTS:
(parse-args 'a AND ('b AND 'bb) AND 'c AND 'f)
; -> '(and a (and (and b bb) (and c f)))
(parse-args 'a AND ('b AND 'bb))
; -> '(and a (and b bb))
However, note that the above code can become impractical when adding other operators.
Edit:
Together with the select macro:
(define-syntax SELECT
(syntax-rules (FROM WHERE)
[(_ select FROM from WHERE where ...)
(list 'Select select 'From from 'Where (parse-args where ...))]))
; TEST:
(SELECT 'somecolumns
FROM 'sometable
WHERE 'something1
AND 'something2
AND ('something3 AND 'something4)
AND 'blop)
; ->
#;'(Select
somecolumns
From
sometable
Where
(and something1
(and something2
(and (and something3 something4) blop))))
Again, pattern-matching allows for cutting the list at the right point to get the rest arguments
I'm learning how the macro system in Scheme works and I'm trying to make my code look more JavaScript-y. So I thought I would start with the function macro. This is how I want a function definition to look:
(function id (x) x)
It should expand to the following:
(define (id x) x)
So I write a macro as follows:
(define-syntax function
(lambda (name args . body)
`(define (,name ,#args) ,#body)))
However when I use it I get the following error (in Chicken Scheme):
Error: during expansion of (define ...) - in `define' - lambda-list expected: (define ((function id (x) x) . #<procedure (rename sym1348)>) #<procedure (compare s11400 s21401)>)
Call history:
<syntax> (function id (x) x)
<eval> (##sys#cons (##core#quote define) (##sys#cons (##sys#cons name args) body))
<eval> (##sys#cons (##sys#cons name args) body)
<eval> (##sys#cons name args) <--
Where am I going wrong? In addition how do I read such error messages to be able to debug the program myself?
In Scheme, using syntax-rules():
(define-syntax function
(syntax-rules ()
((function name (args ...) body ...)
(define (name args ...) body ...))))
The error you are seeing is that apparently Chicken Scheme's compiler expects the second form of define-syntax to be a macro expansion procedure - they typically require arguments for renaming and comparing identifiers. The lambda in your macro doesn't produce a suitable function - syntax-rules does.
The above is guaranteed hygienic.
The way you have defined the macro is not correct as per Chicken documentation. Your code seems to be more inspired by Common Lisp macros. Check out the doc here for define-syntax with transformer function:
The macro should be defined as:
(define-syntax function
(lambda (expr inject compare)
`(define (,(cadr expr) ,#(caddr expr)) ,(cadddr expr))))
expr is the whole macro expression i.e (function id (x) x) and inject and compare are special utility functions passed to the macro while perfoming macro expand.
A new syntax object can be constructed by both syntax/loc and syntax (also written #').
When should I use syntax/loc?
Use #' (i.e. syntax) when you are not constructing a new syntax object, as when you're just referencing a pattern variable bound with syntax-case or with-syntax.
Use #' for temporary syntax objects as in (syntax->list #'(id ...)).
Use #' for syntax objects representing forms that you know won't have syntax errors in them, or where syntax errors in them are the fault of your macro implementation, not the use of your macro.
Use syntax/loc when you construct expressions that potentially can contain syntax-errors due to an incorrect usage of your macro.
Let's consider a concrete example:
The form display-let should work exactly like normal let, except that it displays the values of the bindings before it evaluates the body.
Here is a first implementation:
(define-syntax (display-let-1 stx)
(syntax-case stx ()
[(_ ([id expr] ...) body ...)
#'((lambda (id ...)
(displayln (format "~a is bound to ~a" 'id id)) ...
body ...)
expr ...)]))
Here is an example of a correct use of the macro:
> (display-let-1 ([x 1] [y 2]) (+ x y))
x is bound to 1
y is bound to 2
3
Now let us see what happens when the macro is used incorrectly:
> (display-let-1 ())
lambda: bad syntax in: (lambda ())
This usage is incorrect, since a use of let must always have a non-empty body. Besides printing the error message, DrRacket colors this code red:
(lambda (id ...)
(displayln (format "~a is bound to ~a" 'id id)) ...
body ...)
Although it is correct that the lambda expression constructed by the macro is incorrect, (lambda ()) is not legal, it is not due to an error in the macro, but due to an incorrect use of the macro.
To redirect the blame surround the constructed lambda expression with syntax/loc and use the first argument of syntax/loc as place to color red.
(define-syntax (display-let-2 stx)
(syntax-case stx ()
[(display-let ([id expr] ...) body ...)
#`(#,(syntax/loc stx (lambda (id ...) body ...)) expr ...)]))
> (display-let-2 ())
display-let-2: bad syntax in: (display-let-2 ())
This time around the (display-let-2 ()) entered in the repl is colored red, and the error message mentioned display-let-2 instead of lambda.