> On May 27, 2019, at 22:28, Jonathan Simpson <
jjsi...@gmail.com> wrote:
>
> I may be missing something, but I didn't think I could use the name as valid syntax since this is the form that is creating it. If passing a symbol in would work then I could potentially change my lexer to do that instead of a string.
I might not be understanding your question properly, but I think the answer is: yes, if your macro were to accept an identifier in the `magic-name` position instead of a string, then you could put that identifier in the expansion and it would “just work.” See this macro, for example:[^1]
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (macro-that-defines-a-name stx)
(syntax-parse stx
[(_ some-name)
#'(define some-name 42)]))
Hygiene ensures that `some-name` will be bound wherever the identifier itself comes from, so it will be bound in whatever context uses `macro-that-defines-a-name`:
> (macro-that-defines-a-name my-name)
> my-name
42
If you really want to pass a string to your macro instead of an identifier, you can use what you have in combination with `datum->syntax` to create a new identifier that “copies” scoping information from some piece of input syntax. You currently have this:
(string->symbol (syntax->datum #'magic-name))
...and that will produce a symbol, but it won’t have any scope information associated with it (since `syntax->datum` threw it all away).[^2]
To copy scoping information from the input, you can add a use of `datum->syntax`, supplying the “source” of the copy operation as the first argument:
(let ([magic-name-sym (string->symbol (syntax->datum #'magic-name))])
(with-syntax ([magic-name-id
(datum->syntax #'magic-name magic-name-sym)])
....))
This will create an identifier with the scopes I think you want.
Alexis
[^1]: You should use `syntax-parse` instead of `syntax-case`. There’s nothing wrong with using `syntax-case` per se, but `syntax-parse` is just better on all axes (except perhaps compilation time, but you know what they say about premature optimization). It will provide significantly better syntax error messages even if you stick purely to the `syntax-case` subset.
Also, with `syntax-parse`, you can improve upon the above examples somewhat. You can write a pattern binding of the shape `name:id` or `name:str` to restrict matching to an identifier or string, respectively, and syntax errors will be improved accordingly. Also, you can replace the uses of `let` and `with-syntax` with `syntax-parse`’s built-in `#:do` and `#:with` directives, which are used like this:
(syntax-parse stx
[(_ name:str)
#:do [(define name-sym (string->symbol (syntax->datum #'name)))]
#:with name-id (datum->syntax #'name name-sym)
#'(define name-id 42)])
[^2]: By default, when given a value that isn’t a syntax object, `with-syntax` automatically coerces it to one, copying whatever scoping information is present on the `with-syntax` form itself, i.e. the macro scopes. This is different from explicitly writing
(with-syntax ([foo (datum->syntax #f 'bar)])
....)
since that will bind `foo` to a syntax objects with no scopes at all. In contrast, writing `(with-syntax ([foo 'bar]) ....)` is equivalent to writing `(with-syntax ([foo #'bar]) ....)`.
As a final note, `syntax-parse`’s `#:with` form is slightly different from `with-syntax` in this respect, as it really will use `(datum->syntax #f ....)` instead of trying to guess at what scopes you might have wanted.