How to maintain define functionality with custom #%module-begin? - racket

I want to do something with the strings produced by my custom language, for example to display them. I've created a module-begin something like this:
(define-syntax (module-begin stx)
(syntax-case stx ()
[(_ EXPR ...)
#'(display (apply string-append (filter string? (list EXPR ...))))]))
However, this prevents me from using define in the language. I get the error "define: not allowed in an expression context".
How can I grab the strings without losing the ability to use define and other top-level expressions? Do I need to grab all the define's beforehand and move them to the beginning?

The short answer
Use make-wrapping-module-begin to do the hard work for you.
(require syntax/wrap-modbeg)
(define-syntax module-begin (make-wrapping-module-begin #'wrap-expression))
(define-syntax (wrap-expression stx)
(syntax-case stx ()
[(_ expr) #'(println expr)]))
Change wrap-expression to do whatever you want with expressions. It isn't applied to definitions, require forms, etc. Note that you get the module-body expressions one at a time, not all at once.
The long answer
You do not have the power to register module-level definitions, interpret require forms, etc. Only the macro expander and the primitive #%plain-module-begin form can do that. So your module-begin macro has to cooperate with them.
Your macro must use local-expand to partially expand each module-level form so that you can distinguish between the following:
module-level definitions
require and provide forms
begin sequences, which need to be spliced into the module body
expressions
When you get a definition or require or provide form, you just toss it to the real #%plain-module-begin primitive. The expressions you handle; again, however you like. And for begin forms, your recur on the sub-forms. The code looks something like this:
(define-syntax (module-begin stx)
(syntax-case stx ()
[(_ form ...)
#'(#%plain-module-begin (wrap-module-form form) ...)]))
(define-syntax (wrap-module-form stx)
(syntax-case stx ()
[(_ form)
(let ([e-form (local-expand #'form 'module #f)])
(syntax-case e-form (begin define-syntaxes define-values #%require #%provide)
[(define-syntaxes . _)
e-form]
[(define-values . _)
e-form]
[(#%require . _)
e-form]
[(#%provide . _)
e-form]
[(begin inner-form ...)
#'(begin (wrap-module-form inner-form) ...)]
[expr
#'(wrap-expression expr)]))]))
(define-syntax (wrap-expression stx)
(syntax-case stx ()
[(_ expr) #'(println expr)]))
All the new code is basically what make-wrapping-module-begin is doing for you automatically.

Related

In Racket, can I redefine the form `if` and have other derived forms automatically follow its new semantics?

In a custom Racket language, I would like to change the behavior of the core form if as well as other forms that expand to it (such as and and cond).
Naturally, I could redefine each of these forms but this seems rather redundant. For instance, here is an example where the modified if expects each of its arguments to be wrapped in a list. The macro and is here redefined explicitly.
;; my-lang.rkt
#lang racket/base
(require (for-syntax racket/base))
(provide #%module-begin #%datum #%app
list
(rename-out [car-if if] [car-and and]))
(define-syntax (car-if stx)
(syntax-case stx ()
[(_ c t f) #'(if (car c) t f)]))
(define-syntax (car-and stx) ; this seems redundant
(syntax-case stx ()
[(_) #'#t]
[(_ x) #'x]
[(_ x xs ...) #'(car-if x (car-and xs ...) x)]))
#lang s-exp "my-lang.rkt"
(if (list #f) (list 2) (list 3)) ; => (3)
(and (list #f) (list 2)) ; => (#f)
Is there an easier way to redefine these forms by injecting my new definition of if into the existing definitions provided by racket/base?
The answer is no.
Let's consider the form and. It is defined as a macro (somewhere).
It looks something like:
#lang mumble
(define-syntax (and stx)
(syntax-case stx ()
[(_and) #'#t]
[(_and e) #'e]
[(_and e1 e2) #'(let ([t e1]) (if t e2 #f))]
[(_and e1 e2 e ...) #'(let ([t e1]) (if t (and e2 e ...)))]
[_ (raise-syntax-error 'and "bad syntax" stx)]))
Since the Racket macro system is "referentially transparent" the
identifiers are bound using standard lexical scope rules. That is
the if in the expansion is bound to the if in the module where
the macro is defined. The point is that the macro writer doesn't
need to fear any users redefining any identifiers used in the expansion.
The only way to change the behaviour of the and macro above is to
change the if used. So only if you have access to the definition
of "mumble", you can change the if used. In the case of the standard
Racket and no user can change the definitions.
In short, the answer is "no" due to the design of the macro system.

Transforming a list of symbols to a list of identifiers to be used in a macro

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.

Capturing a variable number of arguments via an ellipsis in a nested macro; Missing pattern variable error

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 ...)]))

Racket Macro Ellipsis Syntax

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 ...)]))

Racket Macro How to Pass Ellipses to Helper function?

Given:
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([body0 (process-body #'(body ...))])
#'body0)]))
How should I receive the pattern and the ellipses in the helper? I'm not even sure if wrapping the body ... inside () is correct, but I've seen it around and it's the only thing that doesn't crash.
The process-body procedure ends up with syntax that has extra () wrapping it. I can try and break this apart, but I'm just wondering what the correct way to do this is.
process-body wraps the body pattern with some code before AND after. And, similar to define, I want to be able to provide the macro with multiple forms rather than all forms in one list. So, if given (form1) (form2), where form2 is the ellipses, process-body should (do-something) (form1) (form2) (do-something-else).
ie,
(define-for-syntax (process-body body-syntax)
(with-syntax ([stx body-syntax])
(syntax/loc body-syntax
(λ (request)
stx))))
Of course I have this working when I define the template in-line, and I suppose I could do that here, but sometimes the template becomes unwieldy and it's nice to call a helper.
Thanks a lot.
As an edit to try dyoo's first example, I'm providing the following:
#lang racket
(define-syntax (test2 stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([(body0 ...) (process-body2 #'(body ...))])
#'(begin body0 ...))]))
(define-for-syntax (process-body2 bodies)
(with-syntax ([(body ...) bodies])
(syntax/loc bodies
(λ (request)
body ...))))
(test2 (print "hi"))
λ: bad syntax
The left hand side of a with-syntax pattern can also have ellipses, so that the following is possible:
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([(body0 ...) (process-body #'(body ...))])
#'(begin body0 ...))]))
The basic idea is that if process-body returns the transformed body elements, we can then introduce them all together with a begin.
Your process-body definition can also use with-syntax with ellipses too. So you can do something like this:
(define-for-syntax (process-body bodies)
(with-syntax ([(body ...) bodies])
(syntax/loc bodies
(λ (request)
body ...))))
If that's the definition of process-body, we should amend test since the shape of the result from process-body is now a complete lambda expression, so we can just return its result directly:
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(process-body (syntax/loc stx (body ...)))]))
As a self-contained example:
#lang racket
(define-syntax (test stx)
(syntax-case stx ()
[(_ body ...)
(process-body
(syntax/loc stx (body ...)))]))
(define-for-syntax (process-body bodies)
(with-syntax ([(body ...) bodies])
(syntax/loc bodies
(λ (request)
(printf "before the body\n")
body ...
(printf "after the body\n")))))
;; Let's try it:
(define p
(test (displayln "hello") (displayln "world")))
(p 'should-be-a-request)