Dynamically binding free identifiers in a function body - racket

Consider a very simple actor language where an actor defines some local state and some methods that can be invoked by sending messages to the actor. In its implementation, one such method of the actor can be transformed into a function that defines the formal parameters of the method and accepts the current local state of the actor. Calling the method returns the new local state.
Binding the formal parameters in the body is no problem, but binding the local state seems to be more difficult. In the example at the end of the code below, in the body of the save method the a will remain unbound, despite an a (a different a) being bound by the generated evaluate-body function in the METHOD macro. The critical point in the code sample below is thus the METHOD macro, more specifically the evaluate-body function (which is where binding should happen, implying that my program design is reasonable)
Is there a way to hygienically bind this arbitrary set of free identifiers (currently only containing a, but it may be anything, really)?
#lang racket
(require (for-syntax syntax/parse))
(require racket/stxparam)
(struct actor (local-state methods))
(struct method (name formal-parameters body))
(define-syntax-parameter local-state-variables #f)
(define-syntax (ACTOR stx)
(syntax-parse stx
[(_ (LOCAL_STATE state-variable ...) method:expr ...+)
#'(syntax-parameterize ([local-state-variables '(state-variable ...)])
; For the sake of simplicity, an actor is currently a list of message handlers
(actor
(make-list (length '(state-variable ...)) (void))
(list method ...)))]))
(define-syntax (METHOD stx)
(syntax-parse stx
[(_ (name:id formal-parameter:id ...) body:expr ...+)
(with-syntax ([(local-state-variable ...) (syntax-parameter-value #'local-state-variables)])
#'(method
'name
'(formal-parameter ...)
(λ (formal-parameter ... #:local-state [current-state '()])
; the "a" that will be bound here is different from the free identifier "a" in the body
(define (evaluate-body local-state-variable ...)
body ...
(list local-state-variable ...))
(apply evaluate-body current-state))))]))
(ACTOR (LOCAL_STATE a)
(METHOD (save new-a)
; "a" is an unbound identifier
(set! a new-a)))

In order for the local state variables to have the proper lexical context, you need to store them as identifiers, not symbols. That is, in the result of the ACTOR macro, you need to change the syntax-parameterize to this:
#'(syntax-parameterize ([local-state-variables #'(state-variable ...)])
#| rest of the template (unchanged)... |#)
Note the replacement of quote/' with syntax/#'. This will store the identifiers with their lexical context instead of as symbols.
The next step is to properly introduce them within the METHOD macro. To do this, you just need to apply syntax-local-introduce to the value of the syntax parameter, which will add the macro introduction scope to the identifiers. You can also replace with-syntax with syntax-parse’s #:with clause to simplify things slightly, so the overall macro becomes this:
(define-syntax (METHOD stx)
(syntax-parse stx
[(_ (name:id formal-parameter:id ...) body:expr ...+)
#:with (local-state-variable ...)
(syntax-local-introduce (syntax-parameter-value #'local-state-variables))
#'(method #| rest of the template (unchanged)... |#)]))
This will work.
The reason syntax-local-introduce is needed here might be a little confusing, but the most intuitive way to think about it is by considering the “sets of scopes” hygiene model that Racket currently uses. In order for macro-introduced bindings to not conflict with user-defined bindings, each piece of syntax returned by a syntax transformer has a fresh scope attached to it, a scope that will never be attached to anything written by the user. Of course, some of the syntax in the result is syntax provided by the user, so the macroexpander needs to ensure it doesn’t attach the fresh scope to those syntax objects.
It’s not possible, in general, to figure out which syntax objects should be considered provided by the user since macro authors can “bend” hygiene and create new syntax objects from other ones. The solution, fortunately, is simple and elegant: just attach the macro introduction scope to all syntax objects provided by the user before handing them off to the macro, then flip the scopes on all pieces of syntax in the result. This way, the user-provided syntax objects will not have the macro introduction scope after the flipping occurs.
The syntax-local-introduce function lets you flip this special scope manually. In this case, since the value of local-state-variables should be treated like an input to the macro, but it isn’t automatically given the macro introduction scope by the macroexpander (since it isn’t a direct input to the macro), you have to add the scope yourself. That way, the macroexpander will remove the scope after the macro is expanded, and the identifier will end up with the proper lexical context.

Related

replacing variables in syntax object

I'd like to replace all occurrences of v1 in a syntax object like #'(or (and v1 v2) (and v1 v3)) to v4 to get #'(or (and v4 v2) (and v4 v3)). What would be the easiest way to do so in Racket? Should I convert the syntax to list or string to replace and convert it back to syntax?
There are several different strategies you can use depending on what the final syntax object is used for. In particular, it depends on whether you can expand the syntax to get a different syntax object with the same behavior, or whether you have to leave everything exactly as it was.
1. If the final syntax object is used as an expression in the output of a macro
If the final syntax object is only used as an expression in the output of a macro, then expanding the syntax object is fine, since the run-time behavior is what matters, not the exact form of the syntax. In this case, you can expand the syntax object in an internal-definition-context that includes the substitution.
;; create a context where x-old is renamed to x-new
(define ctx (syntax-local-make-definition-context))
(syntax-local-bind-syntaxes
(list x-old)
#`(make-rename-transformer (quote-syntax #,x-new))
ctx)
;; expand the syntax in that context
(local-expand stx 'expression '() ctx)
2. If the final syntax object is supposed to be kept exactly as is, and cannot be expanded
If the final syntax object is supposed to be kept exactly as is, except for the substitution, then you can't expand it. You have to traverse it somehow to do the substitution. This comes with several problems if the code you're substituting into might use certain features like quote or syntax->datum. However, there are times when its necessary, and for those times I use a traverse-stx/recur function with this signature:
;; traverse-stx/recur : Stx [Stx -> Stx] -> Stx
;; Traverses `stx`, calling the `recur` function on every sub-piece
(define (traverse-stx/recur stx recur)
....)
Which I use like this:
;; stx-subst : Stx Id Id -> Stx
;; Replaces every instance of `x-old` with `x-new` in the syntax `stx`
(define (stx-subst stx x-old x-new)
;; traverse : Stx -> Stx
(define (traverse s)
(cond [(and (identifier? stx) (free-identifier=? stx x-old))
x-new]
[else
;; pass "yourself" as the recur callback, so that it calls
;; you on every sub-piece
(traverse-stx/recur stx traverse)]))
(traverse s))
The definition of traverse-stx/recur might depend on the language you're traversing, but if it's literally just arbitrary s-expressions with no "meaning" that you're afraid of changing, then it can be structured just like a normal s-expression traversal, though with stx-null?, stx-car, stx-cdr, etc. (from the syntax/stx library) instead of the normal null?, car, cdr, etc.
NOTE: however you define the traversal for your language, a helper function like this might be useful:
;; restore : Stx Any -> Stx
;; Any Any -> Any
(define (restore orig datum)
(if (syntax? orig) (datum->syntax orig datum orig orig) datum))
3. When you need to rely on and preserve the "meaning" in a different core language
In some rare cases, might want to expand to a different "core language" than Racket's core forms. This is still an active area of research and hasn't been totally figured out yet. However, current strategies involve traversing the syntax object manually like (2), while also expanding the syntax using an internal-definition-context like (1), and reconstructing the syntax after the expansion.
The best explanation I've seen so far for how to do this is in this Blog Post by Alexis King. But this is very hard to do correctly, and the more complicated your "core language" is, the harder it becomes.
Use with-syntax.
(with-syntax ([v1 #'v4])
#'(or (and v1 v2) (and v1 v3)))
The output:
#<syntax:3:4 (or (and v4 v2) (and v4 v3))>
Turning this into a macro looks like:
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (replace-id stx)
(syntax-parse stx
[(_replace-id from to so)
(syntax/loc stx
(with-syntax ([from #'to])
#'so))]))
(replace-id v1 v4 #'(or (and v1 v2) (and v1 v3)))
If you want to use replace-id in a macro, then wrap begin-syntax around
the definition in order to define it in phase 1.

Binding a self reference via macros

The project I am working on defines some complex structures that receive messages and run within their own thread. The structures are user-defined and transformed via macros to threads and runtime stuff. Roughly speaking we can say that a complex structure consists of some behaviour that implements the logic, and a procedure for spawning an instance of the behaviour. In the code below I have vastly simplified the situation, where a behaviour defined by the create-thread-behaviour macro is a simple thunk that can be spawned via the spawn macro. I'd like to implement the ability for (an instance of) a behaviour to send messages to itself via a self parameter that would be bound to (current-thread) (~ the thread that is running the behaviour).
I've tried to rig something up using syntax-parameterize but for some reason cannot get it to work. The code below implements a simple application that should clarify what I want to achieve - the special point of interest is the (unimplemented) <self> reference towards the bottom.
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (create-thread-behaviour stx)
(syntax-parse stx
[(_ body:expr ...+)
#'(λ () body ...)]))
(define-syntax (spawn stx)
(syntax-parse stx
[(_ behaviour:id)
#'(thread behaviour)]))
(define behaviour
(create-thread-behaviour
(let loop ()
(define message (thread-receive))
(printf "message: ~a~n" message)
(thread-send <self> "And this is crazy.")
(loop))))
(define instance (spawn behaviour))
(thread-send instance "Hey I just met you")
So the thing with syntax parameters that I tried is the following, which raises the self-defined "can only be used in a behaviour" error. I know I have correctly used syntax parameters before, but perhaps I have just been looking at the problem for too long.
(require racket/stxparam)
(define-syntax-parameter self
(lambda (stx) (raise-syntax-error (syntax-e stx) "can only be used in a behaviour")))
(define-syntax (spawn stx)
(syntax-parse stx
[(_ behaviour:id)
#'(thread
(lambda ()
(syntax-parameterize ([self #'(current-thread)])
(behaviour))))]))
You’re right that syntax parameters seem like the right tool for the job here. There are two issues here around your use of them, however, that are causing the issue. Let’s take them one at a time.
First of all, syntax parameters are semantically just syntax transformers, as you can see by your initial use of define-syntax-parameter, which binds the syntax parameter to a function. Your use of syntax-parameterize, in contrast, binds the syntax parameter to a piece of syntax, which is wrong. Instead, you need to bind it to a syntax transformer as well.
An easy way to achieve the behavior you’re looking for is to use the make-variable-like-transformer function from syntax/transformer, which makes a syntax transformer that, as the name would imply, behaves like a variable. More generally, though, it actually produces a transformer that behaves like an expression, which (current-thread) is. For that reason, your use of syntax-parameterize should actually look like this:
(require (for-syntax syntax/transformer))
(syntax-parameterize ([self (make-variable-like-transformer #'(current-thread))])
(behaviour))
This will avoid the “bad syntax” errors when attempting to use self after it has been parameterized.
However, there’s another problem in your code, which is that it appears to use a syntax parameter like a normal, non-syntax parameter, when syntax parameters don’t work like that. Normal parameters are effectively dynamically scoped, so the use of syntax-parameterize to wrap (behavior) would adjust self within the dynamic extent of the call to behavior.
Syntax parameters, however, don’t work like that. In fact, they can’t: Racket is syntactically a lexically scoped language, so you really can’t have a dynamic syntax binding: all syntax transformers are expanded at compile time, so adjusting a binding during the dynamic extent of a call is impossible. Syntax parameters are entirely lexically scoped, they simply hygienically adjust a binding within a particular scope. In that sense, they are really just like let, except that they adjust an existing binding rather than produce a new one.
With this consideration in mind, it becomes clear that putting the syntax-parameterize form in spawn can’t really work, because behavior is lexically defined outside of spawn. You could just move the use of syntax-parameterize to create-thread-behavior, but now there’s another problem, which is that this wouldn’t work:
(define (behavior-impl)
(define message (thread-receive))
(printf "message: ~a~n" message)
(thread-send self "And this is crazy.")
(behavior-impl))
(define behaviour
(create-thread-behavior
(behavior-impl)))
Now, once again, self is used outside of the lexical extent of syntax-parameterize, so it won’t be bound.
You’ve mentioned that this is a simplified example of what you’re actually doing, so maybe your real example requires a more complicated solution. If so, you may just need to require that self is only bound within the lexical extent of create-thread-behavior. However, your current use of self is remarkably simple, and in fact, it never changes: it’s always (current-thread). For that reason, you could actually just ditch syntax parameters entirely and define self directly:
(define-syntax self (make-variable-like-transformer #'(current-thread)))
Now self will work everywhere as a variable-looking reference to the value of a parameter, current-thread. This might be what you actually want, since it allows the value of self to be truly dynamically scoped (since it uses runtime parameters, not syntax parameters), but it still makes it look like a variable instead of a function.

How to call other macros from a Chicken Scheme macro?

I'm trying to move from Common Lisp to Chicken Scheme, and having plenty of problems.
My current problem is this: How can I write a macro (presumably using define-syntax?) that calls other macros?
For example, in Common Lisp I could do something like this:
(defmacro append-to (var value)
`(setf ,var (append ,var ,value)))
(defmacro something-else ()
(let ((values (list))
(append-to values '(1)))))
Whereas in Scheme, the equivalent code doesn't work:
(define-syntax append-to
(syntax-rules ()
((_ var value)
(set! var (append var value)))))
(define-syntax something-else
(syntax-rules ()
((_)
(let ((values (list)))
(append-to values '(1))))))
The append-to macro cannot be called from the something-else macro. I get an error saying the append-to "variable" is undefined.
According to all the information I've managed to glean from Google and other sources, macros are evaluated in a closed environment without access to other code. Essentially, nothing else exists - except built-in Scheme functions and macros - when the macro is evaluated. I have tried using er-macro-transformer, syntax-case (which is now deprecated in Chicken anyway) and even the procedural-macros module.
Surely the entire purpose of macros is that they are built upon other macros, to avoid repeating code. If macros must be written in isolation, they're pretty much useless, to my mind.
I have investigated other Scheme implementations, and had no more luck. Seems it simply cannot be done.
Can someone help me with this, please?
It looks like you're confusing expansion-time with run-time. The syntax-rules example you give will expand to the let+set, which means the append will happen at runtime.
syntax-rules simply rewrites input to given output, expanding macros until there's nothing more to expand. If you want to actually perform some computation at expansion time, the only way to do that is with a procedural macro (this is also what happens in your defmacro CL example).
In Scheme, evaluation levels are strictly separated (this makes separate compilation possible), so a procedure can use macros, but the macros themselves can't use the procedures (or macros) defined in the same piece of code. You can load procedures and macros from a module for use in procedural macros by using use-for-syntax. There's limited support for defining things to run at syntax expansion time by wrapping them in begin-for-syntax.
See for example this SO question or this discussion on the ikarus-users mailing list. Matthew Flatt's paper composable and compilable macros explains the theory behind this in more detail.
The "phase separation" thinking is relatively new in the Scheme world (note that the Flatt paper is from 2002), so you'll find quite a few people in the Scheme community who are still a bit confused about it. The reason it's "new" (even though Scheme has had macros for a long long time) is that procedural macros have only become part of the standard since R6RS (and reverted in R7RS because syntax-case is rather controversial), so the need to rigidly specify them hasn't been an issue until now. For more "traditional" Lispy implementations of Scheme, where compile-time and run-time are all mashed together, this was never an issue; you can just run code whenever.
To get back to your example, it works fine if you separate the phases correctly:
(begin-for-syntax
(define-syntax append-to
(ir-macro-transformer
(lambda (e i c)
(let ((var (cadr e))
(val (caddr e)))
`(set! ,var (append ,var ,val)))))) )
(define-syntax something-else
(ir-macro-transformer
(lambda (e i c)
(let ((vals (list 'print)))
(append-to vals '(1))
vals))))
(something-else) ; Expands to (print 1)
If you put the definition of append-to in a module of its own, and you use-for-syntax it, that should work as well. This will also allow you to use the same module both in the macros you define in a body of code as well as in the procedures, by simply requiring it both in a use and a use-for-syntax expression.

Overriding "this" identifier macro in Racket

I made my own prototypal OO system in Racket which uses "this" for methods. Everything works great, but unfortunately it prevents Racket's native class system from using "this".
Here's what I want to do:
(require (only-in racket (this oldthis)))
(define-syntax this
(syntax-id-rules (set!)
((set! this ...) (void))
((this a ...) ((self) a ...))
(this (if (null? (self)) oldthis (self)))))
One thing I've tried to do is rename the above "this" macro to be "newthis" and make a helper macro as follows:
(define-syntax (this stx)
(syntax-case stx ()
(this #'(if (null? (self)) oldthis newthis))))
But in both cases Racket throws an error because I'm using "oldthis" outside of a class context. How do I merely reference the old "this" macro without expanding it and throwing an error? Or, more broadly, how can I make my "this" macro delegate to the old "this" macro when (self) is null?
Thanks in advance!
EDIT: Here is some code that uses "this" properly:
(define Foo (new))
(void (# Foo 'name "Foo")
(# Foo 'method (lambda () (# this 'name))))
You can then run Foo's "method" by calling
((# Foo 'method))
Which gives the correct result "Foo"
However, if I use Racket's class system:
(define foo%
(class object%
(super-new)
(define/public (displayself)
(displayln this))))
and run
(define foo (make-object foo%))
(send foo displayself)
I get (), always, since "this" returns (self) and (self) has in its closure a value "self" which is currently null. What I want to do is to check if (self) is null and, if it is, delegate to the built-in "this" macro.
EDIT2:
It looks like this answer is doing what I want to do. When I try it, however, I still get the same error about class context. I think the reason for that is that the #%top macro is invoked by (real-top . rest) and not by real-top on its own. In my case, however, oldthis gets invoked immediately and throws the error. I have no idea how to prevent that.
One thing I did that got close was to enclose oldthis in (eval 'oldthis), but that only worked in the current namespace. In order for that to work consistently, I would have to provide oldthis in conjunction with everything else, which is not only ugly but error prone, since an unsuspecting user of the library wouldn't necessarily understand that they have to pass oldthis all the time for anything to work properly. So I don't want to do it this way.
SOLVED:
(define-syntax this
(syntax-id-rules (set!)
((set! this ...) (void))
((this a ...) ((self) a ...))
(this (if (null? (self)) (eval 'this (module->namespace 'racket)) (self)))))
By providing the racket language as the namespace argument for eval, I can reference the old "this" without making any assumptions about the program using my OO library except that it have access to the racket language.
You should be able to do this directly with rename-in. Take the following code for example:
#lang racket
(require (rename-in racket [this old-this]))
(define this 42)
(define foo%
(class object%
(super-new)
(define/public (get-me)
old-this)))
(define bar (new foo%))
(send bar get-me)
Here we use rename-in to import old-this to point to the same binding as this in Racket. Which is needed because we immediately shadow that binding with: (define this 42)
Next, we define the foo% object, with one method: get-me, which just returns the current object with this (or specifically old-this.
Finally, we define a new foo% called bar, and call get-me. As you can see, it returns the bar object as you would expect.
If, instead of old-this, you returned this, then get-me would return 42, because of the definition. Likewise, if you removed the definition for this as 42, then this would still be bound to the one the class macro expects.

trying to understand require in language extension

I'm trying to define a new language in racket, let's call it wibble. Wibble will allow modules to be loaded so it has to translate it's forms to Racket require forms. But I'm having trouble getting require to work when used in a language extension. I eventually tracked down my problems to the following strange behaviour.
Here's my reader which redefines read and read-syntax
=== wibble/lang/reader.rkt ===
#lang racket/base
(provide (rename-out (wibble-read read) (wibble-read-syntax read-syntax)))
(define (wibble-read in)
(wibble-read-syntax #f in))
(define (wibble-read-syntax src in)
#`(module #,(module-name src) wibble/lang
#,#(read-all src in)))
(define (module-name src)
(if (path? src)
(let-values (((base name dir?) (split-path src)))
(string->symbol (path->string (path-replace-suffix name #""))))
'anonymous-module))
(define (read-all src in)
(let loop ((all '()))
(let ((obj (read-syntax src in)))
(if (eof-object? obj)
(reverse all)
(loop (cons obj all))))))
and here's my much simplified language module, this introduces (require racket/base) into each wibble module
=== wibble/lang.rkt ===
#lang racket/base
(require (for-syntax racket/base))
(provide (rename-out (wibble-module-begin #%module-begin)) #%app #%datum #%top)
(define-syntax wibble-module-begin
(lambda (stx)
(syntax-case stx ()
((_ x ...) #`(#%module-begin (require #,(datum->syntax stx 'racket/base)) x ...)))))
With the above code then this wibble code 'works', i.e. there are no errors
#lang wibble
(cons 1 2)
(cons 3 4)
but the following
#lang wibble
(cons 1 2)
gives error message cons: unbound identifier in module in: cons
Really I'm just looking for an explanation as to what going on. I'm sure the difference is related to this from the racket docs (Racket Reference 3.1)
If a single form is provided, then it is partially expanded in a
module-begin context. If the expansion leads to #%plain-module-begin,
then the body of the #%plain-module-begin is the body of the module.
If partial expansion leads to any other primitive form, then the form
is wrapped with #%module-begin using the lexical context of the module
body; this identifier must be bound by the initial module-path import,
and its expansion must produce a #%plain-module-begin to supply the
module body. Finally, if multiple forms are provided, they are wrapped
with #%module-begin, as in the case where a single form does not
expand to #%plain-module-begin.
but even with that I don't understand why having a single form makes any difference, it's seems to be somthing to do with the timing of partial expansion but I'm not really sure. Nor do I understand why Racket treats a single form as a special case.
Incidentally I can fix the problem with a slight modification to my reader
(define (wibble-read-syntax src in)
#`(module #,(module-name src) wibble/lang
#,#(read-all src in) (void)))
Hard-coding a (void) form means I always have more than one form and eveything works.
Sorry for the long post, I'm just looking for some understanding of how this stuff works.
Alright, I think that I've figured it out.
Your intuition is correct in that the problem lies within the timing of the partial expansion of the single-form module body. Inside of your reader.rkt file, you produce a (module ...) form. As the quoted excerpt from your question states, the forms ... portion of this is then treated specially, since there is only one. Let's take a look at an excerpt from the documentation on partial expansion:
As a special case, when expansion would otherwise add an #%app, #%datum, or #%top identifier to an expression, and when the binding turns out to be the primitive #%app, #%datum, or #%top form, then expansion stops without adding the identifier.
I am almost certain that the partial expansion which occurs at this point does something to the cons identifier. This is the one part that I remain unsure of... my gut tells me that what's happening is that the partial expansion is attempting to find the binding for the cons identifier (since it is the first part of the parentheses, the identifier could be bound to a macro which should be expanded, so that needs to be checked) but is unable to, so it throws a tantrum. Note that even if cons has no phase 1 (syntax-expansion time) binding, the macro expander still expects there to be a phase 0 (runtime) binding for the identifier (among other things, this helps the expander remain hygienic). Because all of this partial expansion happens to the body of your (module ...) form (which is done before your (#%module-begin ...) form where you inject the (#%require ...) form), cons has no binding during the expansion, so the expansion, I believe, fails.
Nevertheless, a naive fix for your problem is to rewrite wibble-read-syntax as follows:
(define (wibble-read-syntax src in)
(let* ((read-in (read-all src in))
(in-stx (and (pair? read-in) (car read-in))))
#`(module #,(module-name src) wibble/lang
(require #,(datum->syntax in-stx 'racket/base))
#,#read-in))
You can then remove the (#%require ...) form from your (#%module-begin ...) macro.
That's not, in my opinion, the best way to fix the issue, however. As a matter of cleanliness, hard-coding in a require form like you've done in wibble/lang.rkt would make Eli Barzilay and co. cry. A much simpler way to do what you are trying to do is by updating your lang.rkt file to something like so:
=== wibble/lang.rkt ===
#lang racket/base
(require (for-syntax racket/base))
(provide (rename-out (wibble-module-begin #%module-begin))
(except-out (all-from-out racket/base) #%module-begin #%app #%datum #%top)
#%app #%datum #%top)
(define-syntax wibble-module-begin
(lambda (stx)
(syntax-case stx ()
((_ x ...) #`(#%module-begin x ...)))))
Writing in this convention removes the need for any hard-coded (require ...) forms and prevents subtle bugs like the one you've unearthed from occuring. If you are confused why this works, remember that you've already provided the #%module-begin identifier using this file, which is subsequently bound in all #lang wibble files. In principle, there is no limit on what identifiers you can bind in this fashion. If you would like some further reading, here's a shameless self-advertisement for a blog post I wrote a little while back on the subject.
I hope I've helped.
The problem is with the require (though I'm not sure I 100% understand all the behavior).
(require X) imports bindings from X with the lexical context of #'X. #'X here has the context of stx, which is the entire #'(module-begin x ...), which is not the context you want. You want the context of one of the cons expressions, i.e., one of the #'xs.
Something like this should work:
(define-syntax wibble-module-begin
(lambda (stx)
(syntax-case stx ()
[(_) #'(#%module-begin)]
[(m x y ...)
#`(#%module-begin
(require #,(datum->syntax #'x 'racket/base))
x y ...)])))
Though, as #belph warned, there's probably a more idiomatic way to accomplish what you want.
The behavior of your original program, and as you intuited, likely has to do with module's different treatment of single and multi sub-forms, but I think the "working" case might be an accident and could be a bug in the racket compiler.