`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.