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.
Related
make-variable-transformer (or make-set!-transformer, as it is called in Racket) can work with identifiers hardcoded in the literal list of the macro definition. The following example with set! invariably comes up when variable transformers are mentioned:
(make-variable-transformer
(λ (stx)
(syntax-case stx (set!)
((set! id _) ...)
(id ...))))
This is nice and all, useful for transparently integrating foreign structures with primitive operations known ahead of time, and it's a bonus that it can work through identifier syntax and rename transformers.
But what I'm wondering is if it's possible to work with syntax dynamically like:
(let-syntax ((# (make-variable-transformer
(λ (stx)
(syntax-case stx ()
((v # i) (vector? #'v) #'(vector-ref v i)))))))
(#(0 1 2) # 1))
=> 1
This doesn't work because the macro call doesn't match the template as syntax-case expects # to be in the initial position since there's no v in the literal list (and it probably assigns # to v pattern variable).
In short: Is it possible to write a syntax transformer that can accomplish this without reader extensions or overriding application, perhaps through a metamacro that rewrites the literal token list of an inner syntax-case (à la Petrofsky extraction)?
NB: The vector example itself is unimportant and I'm not interested in alternative solutions to this exact use-case.
since there's no v in the literal list (and it probably assigns # to v pattern variable).
Not really. set! is a special case that the macro expander handles specifically to make it cooperate with make-variable-transformer. But for other literals, they will fail. E.g.,
(let-syntax ((# (make-variable-transformer
(λ (stx)
(syntax-case stx (v)
((v # i) #'1))))))
(v # 1))
fails with v: unbound identifier.
The second issue with your above code is the side condition (vector? #'v). #'v is a syntax object, so (vector? #'v) will always result in #f. It's unclear what is the right behavior. For example, do you intend for:
(define v (vector 1 2 3))
(v # 1)
to work? If so, a compile-time side condition would be inappropriate, because it's not known if v is a vector at compile-time.
For your main question, the answer is no. It's not possible under the constraints that you imposed. The expansion steps are detailed here, and none of the steps looks beyond the head of the list.
But if we don't care about the constraints. I.e., overriding #%app is OK. It could work.
An issue that you need to think about is, suppose you have (a b c) where b is your kind of macro and a is a regular macro. Who should get the control first? If a should get the control first, you can override #%app to implement this kind of macro. Here's my quick implementation.
#lang racket
(require syntax/parse/define
(only-in racket [#%app racket:#%app])
(for-syntax syntax/apply-transformer))
(begin-for-syntax
(struct my-transformer (t)))
(define-syntax-parser #%app
[(_ x ...)
(define transformer
(for/first ([operand (attribute x)]
#:when (and (identifier? operand)
(my-transformer?
(syntax-local-value operand (λ () #f)))))
(syntax-local-value operand)))
(cond
[transformer (local-apply-transformer
(my-transformer-t transformer)
#'(x ...)
'expression)]
[else #'(racket:#%app x ...)])])
(define-syntax #
(my-transformer
(syntax-parser
[(v _ i) #'(vector-ref v i)])))
(define v (vector 42 1337 1729))
(v # 1) ;=> 1337
Finally, you can always override #%module-begin and simulate the macro expander. It's an overkill solution, but could be appropriate if you want more advanced features, like allowing users to customize precedence so that b is expanded before a.
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.
Let's say I want to replace all occurrences of an procedure at compile time, for example all occurrences of cons with 😄. I tried two options that seemed natural:
1.
(define-syntax 😄
(syntax-rules ()
[(_ x y) (cons x y)]))
This works if I do something like (😄 2 3), but I can't use 😄 if it's not in application position, so I get a 'bad syntax' error if I do (map 😄 '(1 2) '(3 4)).
Then I thought to just replace the identifier itself:
2.
(define-syntax 😄
(λ (_) #'cons))
Now 😄 by itself gives me #<procedure:cons> and (map 😄 '(1 2) '(3 4)) gives the correct answer, but with (😄 2 3) the syntax transformer is taking all the arguments and replaces the whole expression with cons, which is not what I wanted.
How do I achieve what I want? Is there some kind of transformer that does this?
UPDATE: As I typed the last sentence, I invoked the "Rubber duck effect" and found make-rename-transformer which is the thing I want. However, the docs say "Such a transformer could be written manually", and it seems I have failed to do so with my 2 tries. How do you manually make such a transformer? (ignoring the bullet points in the docs for make-syntax-transformer)
Also, I know I can just use (define 😄 cons) but then that's a whole extra function call at runtime.
The absolute easiest (and probably best) way to do this would be to use rename-in to import an identifier under a different name:
(require (rename-in racket/base [cons 😄]))
This is also the most direct way to do it, since it won’t create a syntax transformer at all—it will create a new binding that is identical to cons in every way, including free-identifier=?. From the compiler’s perspective, that makes cons and 😄 indistinguishable.
However, this is a little bit magical. Another approach would be to manually create a rename transformer using make-rename-transformer:
(define-syntax 😄 (make-rename-transformer #'cons))
If you can’t use rename-in, using a rename transformer is the next best thing, since the free-identifier=? function recognizes rename transformers specially, so (free-identifier=? #'cons #'😄) will still be #t. This is useful for macros that care about syntax bindings, since 😄 will be accepted in all the same places cons would be.
Still, this is using some sort of primitive. If you really wanted to, how could you implement make-rename-transformer yourself? Well, the key here is that macro application can appear in two forms in Racket. If you have a macro foo, then it can be used in either of these ways:
foo
(foo ...)
The first case is an “id macro”, when a macro is used as a bare identifier, and the second is the more common case. If you want to write a macro that works just like an expression, though, you need to worry about both cases. You cannot do this with syntax-rules, which doesn’t allow id macros, but you can do this with syntax-id-rules:
(define-syntax 😄
(syntax-id-rules ()
[(_ . args) (cons . args)]
[_ cons]))
This pattern will make 😄 expand as expected in both scenarios. However, unlike the above two approaches, it will not cooperate with free-identifier=?, since 😄 is now a whole new binding bound to an ordinary macro from the macroexpander’s perspective.
From that point of view, is make-rename-transformer magical? Not really, but it is special in the sense that free-identifier=? handles it as a special case. If you wanted to, you could implement your own make-rename-transformer function and your own free-identifier=? function to get similar behavior:
(begin-for-syntax
(struct my-rename-transformer (id-stx)
#:property prop:procedure
(λ (self stx)
(with-syntax ([id (my-rename-transformer-id-stx self)])
((set!-transformer-procedure
(syntax-id-rules ()
[(_ . args) (id . args)]
[_ id]))
stx))))
(define (my-rename-target id-stx)
(let ([val (syntax-local-value id-stx (λ () #f))])
(if (my-rename-transformer? val)
(my-rename-target (my-rename-transformer-id-stx val))
id-stx)))
(define (my-free-identifier=? id-a id-b)
(free-identifier=? (my-rename-target id-a)
(my-rename-target id-b))))
This will allow you to do this:
(define-syntax 😄 (my-rename-transformer #'cons))
…and (my-free-identifier=? #'😄 #'cons) will be #t. However, it’s not recommended that you actually do this, for obvious reasons: not only is it unnecessary, it won’t really work, given that other macros won’t use my-free-identifier=?, just the ordinary free-identifier=?. For that reason, it’s highly recommended that you just use make-rename-transformer instead.
If you manually want to write such a macro, you need to use make-set!-transformer.
http://docs.racket-lang.org/reference/stxtrans.html?q=set-transformer#%28def.%28%28quote.~23~25kernel%29._make-set%21-transformer%29%29
Note that you need to handle both assignments (set! x e) and references x and applications (x arg0 arg1 ...) needs to be handled by the clauses in your syntax-case expression.
UPDATE
In Dybvig's book "The Scheme Programming Language" he has an example of a macro define-integrable that sounds to be just the thing you are after.
http://www.scheme.com/tspl4/syntax.html#./syntax:s61
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.
Background:
I intend to generate debug messages for the code I'm developing. I wrote a macro to avoid writing the logging calls in each function. I know this limits the ability to generate more customized debug messages, but in return it isolates logging from code. And that is what I'm aiming for. This macro approach has other drawbacks too, for example it limits creation of function bindings to this macro only, but I think I can live with that.
Following is the definition of the macro and an example demonstrating its usage.
(define-syntax (define-func stx)
(syntax-case stx ()
[(define-func (func-name args ...) body1 body2 ...)
(if (and (identifier? #'func-name)
(andmap symbol? (syntax->datum #'(args ...))))
(syntax (define (func-name args ...)
(log-debug (format "Function-name ~a:" (syntax-e #'func-name)) (list args ...))
body1
body2 ...))
(raise-syntax-error 'define-func "not an identifier" stx))]
[else (raise-syntax-error 'define-func "bad syntax" stx)]))
(define-func (last l)
(cond [(null? l) null]
[(null? (rest l)) (first l)]
[else (last (rest l))]))
(define-func (main)
(last (list 1 2 3 4 5 6 7 8 9))
(logger))
log-debug and logger are defined in separate module
The output produced is somewhat like following:
Function-name last:
args:
:-> (7 8 9)
Function-name last:
args:
:-> (8 9)
Function-name last:
args:
:-> (9)
Now I want to make it more readable. By readability I mean to provide some kind of indentation so that the person reading the log could make sense of call flow. For example something like following:
Function-name last:
args:
:-> (7 8 9)
Function-name last:
args:
:-> (8 9)
Function-name last:
args:
:-> (9)
It is easier to figure out who called whom and so forth. I have an idea that can do this. It involves a variable that keeps track of indentation then after logging the function name I will increase the indent and after evaluation of body and before returning the value decrements the value. Something like following:
(define indent 0)
(define-syntax (define-func stx)
(syntax-case stx ()
[ (... ...)
(...
(log-debug ...)
(increment indent)
(let [(retval (body1 body2 ...)]
(decrease indent)
retval))]))
increment and decrease increases and decreases indentation respectively.
Problem:
It works even for function that returns void. I'm not sure whether its the correct behavior. In racket void is a special value, but I'm not sure that creating a binding to void is right way to go.
Is there any better way to achieve the same? If not are there any problems in this design? I'm open to any idea/change as long as they keep the logging and code separate.
thanks for the help!
I have several suggestions for you:
It's probably better to use a parameter instead of a variable, for "global" stuff like your indentation level, since the original value is restored for you at the end of the parameterize expression.
All those raise-syntax-error checks you have in your macro are totally superfluous: syntax-case already provides guards (also known as fenders) that allow you to do any validation of macro "arguments" necessary:
(define-syntax (define-func stx)
(syntax-case stx ()
[(_ (func-name args ...) body1 body2 ...)
(andmap identifier? (syntax->list #'(func-name args ...)))
#'(define (func-name args ...)
(log-debug (format "Function-name ~a:" 'func-name)
(list args ...))
body1
body2 ...)]))
I've also fixed up your code in several places, as you can see above:
I used (_ ...) instead of (define-func ...), since in syntax-case (unlike syntax-rules), the latter will actually bind a pattern variable called define-func, which will affect any recursive macro calls you may want to do (I'll grant that you don't have one here, but it's a good habit to have anyway).
Rather than completely flatten the #'(args ...) in the guard, I just turned it into a list of syntax objects so you can test using identifier?. This is more intention-revealing than testing using symbol?, and allows us to also test func-name in the same expression.
You don't need to use (syntax-e #'func-name) inside the expanded code! Just quote it.