Thanks Taylan, gmail is on the fritz lately and doesn't show replies until after I post; let me read what you wrote and ponder. ~~~
On Sat, Jan 11, 2020 at 3:56 PM Taylan Kammer <taylan.kam...@gmail.com> wrote: > 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 > -- cassette tapes - analog TV - film cameras - you