`On Tue, Sep 10, 2019 at 1:19 AM Simon Haines <
simon.hai...@con-amalgamate.net> wrote:

> This is a rather unpleasant pitfall of the REPL. If you try to evaluate
>> the expression `(if #f some-unbound-identifier 1)`, you will see that it
>> evaluates to `#f` in the REPL but raises an unbound identifier error in a
>> module. At the REPL, `some-unbound-identifier` refers to a top-level
>> variable, and it is allowed to support, for example, forward references to
>> identifiers that will be defined in a subsequent interaction, or
>> interactive re-definition of variables.
>>
>
> When entering '(hex a b c 1 2 3)' into the REPL, I don't think the symbols
> 'a', 'b' and 'c' are undefined or forward-references as they appear in the
> taken branch of the conditional. Maybe syntax objects are different coming
> from the REPL than a module, somehow resulting in the macro working as
> expected?
>

When you mention symbols being defined or undefined, which is terminology
that other Lisp and Scheme languages use, I think there can be some
confusion between a few different concepts. In Racket, we reserve the word
"symbol" for a type of value, like a number, string, or boolean. So, in the
expression `(λ (f) (f f))`, there are no symbols: we call `λ` and `f`
"identifiers," which are the parts of the syntax of a program that might
have bindings. Of course you know about `quote`, which can produce a symbol
from literal program text in expressions like `(quote a)` or, more often,
`'a`. Lisp programs that manipulate other programs, most particularly
macros, traditionally used symbols as the representation for identifiers,
but it turns out that a symbol isn't really as much information as you
want, so Racket uses "syntax objects," where a syntax object combines a
datum with lexical context (a set of scopes), source-location information,
and other properties. By analogy to `quote`, the expression `(syntax a)` or
`#'a` produces a syntax object, and in particular a syntax object
representing an identifier. (Sometimes we say "identifier" when we really
mean "a syntax object representing an identifier," which can be a bit
confusing, but then `identifier?` is probably a better function name than
`syntax-object-representating-identifier?`.)

If you consider the expanded form of `(hex a)`:

> (bytes
>   (let ([e (syntax-e #'a)])
>     (if (number? e)
>         a
>         (string->number (symbol->string e) 16))))
>

The first sub-expression to be evaluated is `#'a`, which produces a syntax
object representing an identifier. This syntax object value is then passed
to the procedure `syntax-e`, which extracts the symbol value.

The key point here is that this doesn't involve a reference to the
identifier `a`: it could be written equivalently as `'a` (and should be, if
you want your macro to work this way), but we can also write it as
`(string->symbol "a")`, which makes it extra clear that the binding of `a`,
or rather the lack thereof, isn't consulted in evaluating this expression.
So we could re-write the expansion of `(hex a)` as:

> (bytes
>   (let ([e (string->symbol "a")])
>     (if (number? e)
>         a
>         (string->number (symbol->string e) 16))))
>

This makes it clear that the only reference to the identifier `a` is in the
then branch of the conditional, which isn't taken. Probably, in your
original macro, you wanted to write `e` there instead of the template
variable `num`.

-Philip

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/CAH3z3gaQjvtz4S4DnR0NxVgPMVQ_jJgfca1kQKOkggvDp6exaQ%40mail.gmail.com.

Reply via email to