I have a macro that's working when one argument is passed, and I'd like to expand it to accept n number of arguments using ..., but I'm having trouble figuring out the syntax.
The macro accepts either custom syntax, ie, key:val key:val, or it accepts a procedure.
For example: (3 different usages)
(schema-properties [(name:first-name type:string)])
(schema-properties [(name:age type:number required:#t)])
(schema-properties [(my-custom-fn arg1 arg2 arg3)])
Definition:
(define-syntax (schema-properties stx)
(syntax-parse stx
[(_ [(prop:expr ...)])
(with-syntax ([prop0 (make-prop-hash #'(prop ...))])
#'(list prop0))]))
(define-for-syntax (make-prop-hash stx)
(with-syntax ([(props ...) stx])
(if (regexp-match #px":"
(symbol->string (car (syntax->datum #'(props ...)))))
#'(pairs->hash 'props ...)
#'(props ...))))
This works, in that it checks the prop:expr syntax for the presense of ":", and if it exists, passes it to the function (pairs->hash 'props ...), otherwise, it just invokes it (props ...).
Now, I'd like to be able to pass in:
(schema-properties [(name:first-name type:string)
(name:last-name type:string)
(my-fn arg1 arg2 arg3)])
and have it work the same way. But I'm currently in ellipsis hell and my brain is no longer working correctly.
Any insights are appreciated.
Recommendation: use helper functions to help deal with nesting. Your schema-properties macro knows how to deal with one level of nesting, and you want to apply that to multiple clauses. It's the same principle as when we deal with lists of things: have a helper to deal with the thing, and then apply that across your list. It helps cut down complexity.
For your code, we can do it like this:
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (schema-properties stx)
(syntax-parse stx
[(_ [clause ...])
(with-syntax ([(transformed-clauses ...)
(map handle-clause (syntax->list #'(clause ...)))])
#'(list transformed-clauses ...))]))
;; handle-clause: clause-stx -> stx
(define-for-syntax (handle-clause a-clause)
(syntax-parse a-clause
[(prop:expr ...)
(make-prop-hash #'(prop ...))]))
(define-for-syntax (make-prop-hash stx)
(with-syntax ([(props ...) stx])
(if (regexp-match #px":"
(symbol->string (car (syntax->datum #'(props ...)))))
#'(pairs->hash 'props ...)
#'(props ...))))
;;; Let's try it out. I don't know what your definition of pairs->hash is,
;;; but it probably looks something like this:
(define (pairs->hash . pairs)
(define ht (make-hash))
(for ([p pairs])
(match (symbol->string p)
[(regexp #px"([-\\w]+):([-\\w]+)"
(list _ key value))
(hash-set! ht key value)]))
ht)
(schema-properties [(name:first-name type:string)
(name:last-name type:string)
(list 1 2 3)])
Another recommendation: use syntax classes to help deal with nesting:
First, define a syntax class that recognizes key:value identifiers (and makes their component strings available as key and value attributes):
(begin-for-syntax
(define-syntax-class key-value-id
#:attributes (key value)
(pattern x:id
#:do [(define m (regexp-match "^([^:]*):([^:]*)$"
(symbol->string (syntax-e #'x))))]
#:fail-unless m #f
#:with (_ key value) m)))
Now define a clause as either a sequence of those (to be handled one way) or anything else (to be treated as an expression, which must produce a procedure). The code attribute contains the interpretation of each kind of clause.
(begin-for-syntax
(define-syntax-class clause
#:attributes (code)
(pattern (x:key-value-id ...)
#:with code #'(make-immutable-hash '((x.key . x.value) ...)))
(pattern proc
#:declare proc (expr/c #'(-> any))
#:with code #'(proc.c))))
Now the macro just puts the pieces together:
(define-syntax (schema-properties stx)
(syntax-parse stx
[(_ [c:clause ...])
#'(list c.code ...)]))
Related
Consider the scenario where I would like to specify a very simplistic actor language using Racket macros. An actor is defined by a behaviour that defines some local state and message handlers that implement some logic. The body of a message handler can use both the formal parameters of the message, as well as the state variables. An example is implemented in the code below.
There is quite a lot of context in the code which is probably not even necessary. However, I have included it regardless in order to provide a running example, and the fact that I need to use syntax-parametrize may complicate the solution. The special point of interest is the with-syntax clause in the MESSAGE macro, where I require the (local-state-variable ...) pattern to match a list of identifiers, currently #'local-state-variables which is a list of symbols (bound by syntax-parameterize in the ACTOR macro), and thus does not match. So far I have not been able to find the solution, although it does not seem like it should be shockingly difficult. Am I missing something obvious?
#lang racket
(require (for-syntax syntax/parse))
(require racket/stxparam)
(define LOCAL_STATE
(lambda (stx)
(raise-syntax-error 'LOCAL_STATE "should only be used inside an actor" stx)))
; Define some syntax classes because abstractions are nice
(begin-for-syntax
(define-syntax-class actor-local-state
#:description "actor local state"
#:literals (LOCAL_STATE)
(pattern (LOCAL_STATE state-variable:id ...)))
(define-syntax-class message-pattern
#:description "actor message pattern"
(pattern (identifier:id argument:id ...))))
(define-syntax-parameter local-state-variables
(lambda (stx)
(raise-syntax-error 'local-state-variables "reserved keyword for actors" stx)))
(define-syntax (MESSAGE stx)
(syntax-parse stx
[(_ pattern:message-pattern body:expr ...+)
; Currently there is a "binding match failed" error on the following line, but replacing #'local-state-variables with #'(a b) (a list of identifiers) needless to say works.
(with-syntax ([(local-state-variable ...) #'local-state-variables])
; For simplicity just display the state variables - this is normally where some magic happens
#'(display '(local-state-variable ...)))]))
(define-syntax (ACTOR stx)
(syntax-parse stx
[(_ state:actor-local-state handler:expr ...+)
#'(syntax-parameterize
([local-state-variables '(state.state-variable ...)])
; For the sake of simplicity, an actor is currently a list of message handlers
(list handler ...))]))
; in this proof-of-concept code this should print (a b)
(define behaviour
(ACTOR (LOCAL_STATE a b)
(MESSAGE (add x y) (+ a b x y))))
Use syntax-parameter-value. Here's an example of using syntax parameters to manage lists of variables:
;; vars : syntax parameter of (Listof Identifier)
(define-syntax-parameter vars null)
;; with-vars: like let, but set vars
(define-syntax (with-vars stx)
(syntax-parse stx
[(_ ([var:id rhs:expr] ...) . body)
#'(let ([var rhs] ...)
(syntax-parameterize ([vars (list (quote-syntax var) ...)])
. body))]))
;; get-vars: get vars (symbolic name) and their values
(define-syntax (get-vars stx)
(syntax-parse stx
[(_)
(with-syntax ([(var ...) (syntax-parameter-value #'vars)])
#'(list (list (quote var) var) ...))]))
;; Examples:
(get-vars)
;; => '()
(with-vars ([x 1])
(get-vars))
;; => '((x 1))
(with-vars ([x 1])
(with-vars ([y 2] [z 3])
(set! z 17)
(get-vars)))
;; => '((y 2) (z 17))
The easiest way to turn any datum (including a list of symbol) into an identifier with datum->syntax. (You can also use format-id, but that works on only a single identifier.) With these functions, you pass in a syntax object for the scopes you want your new identifier to have, or #f if you want it to inherit the scopes that your current macro is generating.1 Getting your list of identifiers (as one single syntax object, would just be:
(syntax->datum stx '(a b c))
Where '(a b c) is your list of identifiers. Finally, you can then add this in your with-syntax:
(with-syntax ([(local-state-variables ...) (datum->syntax stx ...)])
...)
As a side note, the way to answer the title of your question, just iterate over your list with map producing a new list using format-id:
(map (curry format-id stx "~a") '(a b c)
1Unless I'm wrong, if so, please correct this.
Consider a scenario of two macros: the outer-macro defines a general structure of some entity, and the inner-macro expands in the scope of the outer macro. My intent is captured in the following code, where the expected output is a print statement. This example throws the following error for the pattern of the inner macro: (_ value ...).
syntax: no pattern variables before ellipsis in template in: ...
I intend to use value ... in the same way as the body ... pattern of the outer macro. In fact, a list of the 'values' is exactly what I need (not necessarily a very flexible 'ellipsis pattern'). Sadly it does not work this way. How can I capture a variable amount of arguments in the inner macro?
#lang racket
(require
racket/stxparam
(for-syntax syntax/parse))
(define-syntax-parameter inner-macro
(lambda (stx)
(raise-syntax-error 'inner-macro "generic error message" stx)))
(define-syntax (outter-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(syntax-case stx ()
[(_ value ...)
(printf "values are: ~a~n" (list value ...))]))])
body ...)]))
(outter-macro
(inner-macro 'a 'b 'c))
; expected result
; > "values are: (a b c)"
To “escape” ellipses in syntax templates, you can use the syntax (... <form>), where <form> is a syntax template where ... sequences are treated literally. Therefore, you can wrap a piece of syntax to include literal ellipses:
> #'(... (syntax-rules ()
[(x ...) (list x ...)]))
#<syntax:4:9 (syntax-rules () ((x ...) (li...>
You can use this to surround your inner macro definition to escape the inner ellipses:
(define-syntax (outer-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(... (syntax-case stx ()
[(_ value ...)
(printf "values are: ~a~n" (list value ...))])))])
body ...)]))
However, this is actually not quite right, because your syntax-case body is wrong—it does not return a syntax object. You are just missing a #' before the (printf ...) (or you could use syntax-rules), so the correct implementation should be the following:
(define-syntax (outer-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(... (syntax-case stx ()
[(_ value ...)
#'(printf "values are: ~a~n" (list value ...))])))])
body ...)]))
This should work as intended.
Alexis King's answer is good. However another way to do it, which I find simpler to think about, is to use a #:with pattern (or a with-syntax), to define something like ooo as a literal ellipsis.
You can create a literal ellipsis with quote-syntax, so the #:with clause looks like #:with ooo (quote-syntax ...). Then you use ooo whenever you want to generate an ellipsis in the output of the macro.
(define-syntax (outer-macro stx)
(syntax-parse stx
[(_ body:expr ...+)
#:with ooo (quote-syntax ...)
#'(syntax-parameterize
([inner-macro
(lambda (stx)
(syntax-case stx ()
[(_ value ooo)
#'(printf "values are: ~a~n" (list value ooo))]))])
body ...)]))
I'm trying to define a macro that generates an anonymous function taking one argument named it, for succinctness, so that instead of
(λ (it) body)
I can write
(λλ body)
(In other words, (λλ body) transforms to (λ (it) body))
(define-syntax-parameter it #f)
(define-syntax λλ
(syntax-rules ()
((_ body)
(λ (x) (syntax-parameterize ((it x)) body)))))
(λλ (< it 0)) ; For testing
I get operators.rkt:13:28: ?: literal data is not allowed; no #%datum syntax transformer is bound in the transformer environment in: #f at (define-syntax-parameter if #f), but as far as I can tell, this is exactly like the example given in racket's doc for how to use define-syntax-parameter. I can suppress the error by replacing #f with a function (I used member, but not for any real reason), but after doing that, I get operators.rkt:17:38: x: identifier used out of context in: x. What am I doing wrong?
You left out the syntax-id-rules part in the example. It's the part that specifies that it should expand to x. Alternatively, you can use make-rename-transformer:
#lang racket
(require racket/stxparam)
(define-syntax-parameter it #f)
(define-syntax λλ
(syntax-rules ()
((_ body)
(λ (x) (syntax-parameterize ([it (make-rename-transformer #'x)]) body)))))
((λλ (< it 0)) 5)
((λλ (< it 0)) -5)
=>
#f
#t
Syntax parameters are not the only way to implement the macro you have in mind. A simpler (IMO) way is to just use datum->syntax to inject the identifier it:
(define-syntax (λλ stx)
(syntax-case stx ()
((_ body ...)
(with-syntax ((it (datum->syntax stx 'it)))
#'(λ (it) body ...)))))
To use your example:
(define my-negative? (λλ (< it 0)))
(my-negative? -1) ;; => #t
I created this:
(define-syntax (with-hash stx)
(syntax-parse stx
[(_ obj:id ((~or key:id [new-key:id hash-key:id]) ...) body:expr ...+)
#'(let ([key (hash-ref obj 'key)] ...
[new-key (hash-ref obj 'hash-key)] ...)
(begin body ...))]))
So that I can do this:
(require rackunit)
(define h (hash 'id 1 'name "scott"))
(with-hash h (id [new-name name])
(check-equal? id 1)
(check-equal? new-name "scott"))
How can I add an alternative pattern that automatically binds all the hash keys locally without the client specifying them in the call?
ie:
(define h (hash 'id 1 'name "scott"))
(with-hash h
(check-equal? id 1)
(check-equal? name "scott"))
I suspect it involves renaming transformers, but am I able to declare syntax parameters and rename them dynamically, based on the runtime hash?
Also, I thought something like this might be on the right track:
(define-syntax (with-hash stx)
(syntax-parse stx
[(_ obj:id (key:id ...) body:expr ...+)
#'(let ([key (hash-ref obj 'key)] ...)
(begin body ...))]
[(_ obj:id body:expr ...+)
#'(with-hash obj (id title) body ...)]))
where I recall the macro and parse out the datums to be bound, but in that case, the id and title variables are not bound, even though the macro works otherwise.
Clearly I'm missing something in my understanding.
Any insights are appreciated.
Thanks.
You can't, really. Variable scoping is a static property, and a hash's keys are a dynamic property, so any solution is going to be wrong. But since you asked, there are two wrong solutions that are vaguely similar to what you're asking for.
One thing you could do is use eval. But when you call eval you will have lost any local variables; see the docs. You can probably work the code out yourself.
Another thing you could do is change the meaning of unbound variable references by shadowing #%top, which is the syntax implicitly wrapped around variable references to unbound (or "bound by the top level environment, maybe") variables. But that means that with-hash will fail to shadow any keys that already have a local or module-level binding. Here's what the code looks like, anyway:
(define-syntax (with-hash stx)
(syntax-case stx ()
[(with-hash h . body)
(with-syntax ([#%top (datum->syntax stx '#%top)])
#'(let-syntax ([#%top
(syntax-rules ()
[(#%top . x)
(hash-ref h 'x)])])
(begin . body)))]))
Dang, Ryan responded while I was trying to come up with an answer :) Here is a solution with eval anyways, with the same caveat that others have already expressed.
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (with-hash stx)
(syntax-parse stx
[(_ h:expr body:expr ...+)
#'(begin
(define-namespace-anchor a)
(let ([keys (hash-keys h)])
(define (mk-bind k) `[,k (hash-ref h (quote ,k))])
(eval
`(let ,(map mk-bind keys)
,#(quote (body ...)))
(namespace-anchor->namespace a))))]))
(require rackunit)
(define h (hash 'id 1 'name "scott"))
(with-hash h
(check-equal? id 1)
(check-equal? name "scott"))
EDIT:
As an alternative, you can fake it with something like this if you know you are only going to use it in a specific way.
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (with-hash stx)
(syntax-parse stx #:datum-literals (check-equal?)
[(_ h:expr (check-equal? key:id val:expr) ...)
#'(let ([keys (hash-keys h)])
(check-true (hash-has-key? h (quote key))) ...
(check-equal? (hash-ref h (quote key)) val) ...)]))
(require rackunit)
(define h (hash 'id 1 'name "scott"))
(with-hash h
(check-equal? id 1)
(check-equal? name "scott"))
I'd suggest going the other direction and sticking with providing the identifiers. It is always a bit suspicious when identifiers are created/added into the evaluation environment in a Scheme program. Yes it is allowed and can be done safely, but it confuses ones understanding of what is bound, when and where.
So instead I'd suggest thinking of your with-hash as a binding construct which allows access to the fields in hash. Used like this:
(with-hash h ((the-id 'id) (the-name 'name)) ...)
or, using the default names,
(with-hash h (id name) ...)
It would be implemented like this:
(define-syntax with-hash
(syntax-rules ()
((_ "gen" hash ((fname fkey) ...) body ...)
(let ((obj hash))
(let ((fname (hash-ref obj 'fkey)) ...)
body ...))))
...
))
I am working on a language translator in guile scheme, and need to handle the basic case, where you're trying to convert a single word.
(define var 5)
(translate var)
This should return the string var and not the number 5.
How do I do this using R5RS Scheme macros (the define-syntax style)?
Edit:
I'm translating from Scheme to Coffeescript.
(define-syntax translate
(syntax-rules ()
[(_ v) 'v]))
And if you want a string:
(define-syntax translate
(syntax-rules ()
[(_ v) (symbol->string 'v)]))
Hopefully Guile's compiler is smart enough to fold the resulting expression so it essentially becomes a constant string.
With syntax-case and its guard support:
(define-syntax translate
(lambda (stx)
(syntax-case stx ()
[(_ v) (identifier? #'v)
#'(symbol->string 'v)]
[(_ v) (number? (syntax-e #'v))
#'(number->string v)])))
(I've used square brackets for easy comparison with Eli's answer, however, it's not my usual style. ;-))
But if you're using syntax-case, then you can just as well do the conversion at the syntax level instead of producing code that does it at runtime:
(define-syntax translate
(lambda (stx)
(syntax-case stx ()
[(_ v) (identifier? #'v)
(datum->syntax stx (symbol->string (syntax->datum #'v)))]
[(_ v) (number? (syntax-e #'v))
(datum->syntax stx (number->string (syntax->datum #'v)))])))
The main thing here is that the macro code is now plain scheme, for example, you could abstract the common parts into a helper:
(define-syntax translate
(lambda (stx)
(define (rewrap convert x)
(datum->syntax stx (convert (syntax->datum x))))
(syntax-case stx ()
[(_ v) (identifier? #'v) (rewrap symbol->string #'v)]
[(_ v) (number? (syntax-e #'v)) (rewrap number->string #'v)])))
Along the same lines, if this macro is so simple, then there's no real need for syntax-case, other than pulling out the subexpression:
(define-syntax translate
(lambda (stx)
(syntax-case stx ()
[(_ v) (let ([d (syntax->datum #'v)])
(datum->syntax
stx
((cond [(number? d) number->string]
[(symbol? d) symbol->string])
d)))])))
Note, BTW, that there is no magic in syntax-case -- and in the case of this simple pattern, you could just pull out the value yourself:
(define-syntax translate
(lambda (stx)
(let ([d (cadr (syntax->datum #'v))])
(datum->syntax
stx
((cond [(number? d) number->string]
[(symbol? d) symbol->string])
d)))))
There is some boilerplate stuff that syntax-case does that this last version loses:
If you use the macro in an unexpected way like (translate) then this version will throw an error about cadr instead of a more comprehensible syntax error
Similarly, if you use (translate 1 2) then this version will just silently ignore the 2 instead of an error.
And if it's used with something that is neither an identifier nor a number (eg, (translate (+ 1 2))) then this will depend on the unspecified value that cond returns rather than throwing a syntax error.
The other answers are useful enough already, but I thought I'd just point out that it's possible to generalize this technique in a very useful ways: macros to print out expressions and their results for debugging:
(define-syntax log-expr
(syntax-rules ()
((_ expr)
(let ((result expr))
(write (quote expr))
(display " evaluates to ")
(write result)
(newline)
result))))