On 11.01.2020 19:52, Linas Vepstas wrote: > Or, thinking aloud a bit: boxes and symbols.... > > So, for example, if I was able to tell apart calls (f 42) from calls (f x) > where x is a "symbol" (or "variable", *see below*) referencing an integer, > then, for the former case, I could create a new symbol (in guile) and > attach it to a box, fill that box with whatever (f 42) would have returned. > Thence-forward, any call site in the guile code that had (f 42) in it would > get replaced by the symbol (or the unboxed value)... > > At the moment, I don't know how to tell apart 42, the literal number, from > x, a symbol that references a number. (Somehow, I can't make symbol? work > ...) I also don't know how to "edit" the call site (the cell, the box?) > that has (f 42) as the call-target, and replace it by a constant (or a > boxed constant). > > But my naive thinking fails: > (define x 0) > (symbol? x) => #f > (variable? x) => #f > > So I guess that x is not a symbol, from the guile point of view!? This is > .. confusing. What is x, then, if not a symbol?
The issue here is that the expression "(symbol? x)" first evaluates the expressions "symbol?" and "x" and then uses their values to go on. So it ends up being: (<value-of-symbol?> <value-of-x>) So by the time the procedure behind "symbol?" is called, it neither knows that it was called "symbol?" nor does it know that the value it received, i.e. the integer 0, came from a variable called "x". What you want to do is delve down to the macro layer so to say, by using "define-syntax" and ideally "syntax-case". Here's a demonstration: (define-syntax symbol-syntax? (lambda (stx) (syntax-case stx () ((_ x) (if (symbol? (syntax->datum #'x)) #'(display "yep\n") #'(display "nope\n")))))) scheme> (symbol-syntax? blah) yep scheme> (symbol-syntax? (blah)) nope What happens here is the following. Going through it part by part. (define-syntax symbol-syntax? (lambda (stx) ... You register a procedure (lambda (stx) ...) as a macro which you bind to "symbol-syntax?". Now the expression "(symbol-syntax? blah)" will not be evaluated in the regular fashion, because Guile sees that "symbol-syntax?" is registered as a macro. Instead of trying to get the values of "symbol-syntax?" and "blah" like it happened with "(symbol? x)", this time Guile calls the procedure registered with "symbol-syntax?" immediately, and passes it a "syntax object" that represents the whole expression "(symbol-syntax? blah)". (Not just the "blah" argument but the whole expression, don't ask why.) (syntax-case stx () ((_ x) ... Within the procedure, we use "syntax-case" to do pattern-matching on the expression "(symbol-syntax? blah)" that is stored in the variable "stx". We know that the first thing in the expression is "symbol-syntax?" because otherwise we wouldn't have been invoked in the first place, so we want to ignore that, hence we use the pattern "(_ x)" which ignores the first thing (via the underscore) and binds the second thing to "x". That second thing is "blah" so "x" contains that now. (if (symbol? (syntax->datum #'x)) ... Now "x" contains the element "blah" but to your probable surprise, it's not a symbol but rather a syntax object. Thankfully we can just call "syntax->datum" to get the corresponding data value, which is going to be the symbol "blah". You're probably wondering why we wrote #'x instead of just x in our call to "syntax->datum" and to be honest I'm not 100% clear on the reason to this day, but it's how syntax-case expects you to use pattern variables. You're never allowed to reference them "bare". #'(display "yep\n") Finally, we use #' to create a whole new syntax object, containing the expression (display "yep\n"). That syntax object is the value returned from our procedure, so Guile puts it where "(symbol-syntax? blah)" was. When you compile the code, all that actually ends up in the program is "(display ...)" with no trace of the original "(symbol-syntax? ...)" expression being left. That's what macros do; they replace code at compile time and leave no trace of themselves. In the second call, "(symbol-syntax? (blah))", the pattern variable "x" ends up containing a syntax object representing "(blah)", and calling "syntax->datum" on that yields a list and not a symbol, hence we end up returning '(display "nope\n")' from the macro. ---------- All that might be daunting at first but after a while it becomes natural, and the power it gives the programmer is really like nothing else. :-) What you intend to do, which is traverse through a whole lambda body and find instances of "(f 42)" to replace them, might be a bit tricky. Let's say you've written a macro "my-define" which does that, then consider the following definition: (my-define (foo x) (let ((f 42)) (+ f x))) Now you probably don't want to turn that "(f 42)" into anything else, because it's not really a call to your "f". I've never written a macro yet which does something like that, so I'm not sure I can help, but I'm happy to respond to questions about the general workings of the macro system. - Taylan