Arc's gensyms are currently broken. Maybe this is old news to some of you, but I was quite surprised when I discovered this. The essential problem is that gensyms created in arc are not unique symbols, they are merely created from a string prefix and a counter, and if someone happened to use a symbol with the same name as the printed representation of the gensym, there would be a name conflict. (define (ar-gensym)
(set! ar-gensym-count (+ ar-gensym-count 1))
(string->symbol (string-append "gs" (number->string ar-gensym-count))))
In arc, if gensyms were truly unique, the last part of the following would fail. arc> (= g (uniq))
gs1767
arc> (eval `(= ,g "foo"))
"foo"
arc> (eval g)
"foo"
arc> gs1767 ; this would result in an error if g was not 'gs1767
"foo"
arc> (= gs1767 "bar")
"bar"
arc> gs1767
"bar"
arc> (eval g) ; this would still be "foo" if g was a unique symbol
"bar"
This behavior isn't surprising considering that uniq just increments a counter and creates a new symbol from the resulting string. (And a comment above the definition admits the current implementation is something of a joke.)This problem does not occur in mzscheme; symbols created with gensym, and symbols that happen to have the same name, are distinct and separate. > (define g (gensym))
> g
g2
> (eval (list 'define g "foo"))
> (eval g)
"foo"
> g2
reference to undefined identifier: g2
> (define g2 "bar")
> g2
"bar"
> (eval g)
"foo"
Changing the definition of 'ar-gensym in ac.scm to the following seems to fix half the problem: (define ar-gensym gensym)
The part fixed is for local variables declared as function parameters (and therefore 'let and 'with). However, for global variables, the problem persists because of arc's explicit addition of a prefix, which results a symbols that does not act as a gensym. (define (ac-global-name s)
(string->symbol (string-append "_" (symbol->string s))))
I believe that global gensyms will work properly if we simply don't attempt to add the prefix. I don't know of any way to definitively determine if a symbol is a gensym, but my best guess at this point is to convert the symbol to a string and back again, and if the two symbols are equal, then the original is an interned symbol (and thus not a gensym). The following change to 'ac-global-name seems to fix the behavior of the global gensym test case above. (define (ac-global-name s)
(if (equal? s (string->symbol (symbol->string s)))
(string->symbol (string-append "__" (symbol->string s)))
s))
So I guess my question is, is this (assuming the last hack is correct) the right way to solve the gensym issue? If so, how do you determine if a symbol is a gensym or not? (Is there a better way to determine if a symbol has been interned?) Are there any other reasons why pg didn't want to use mzscheme's 'gensym?If people like the idea of using mzscheme's gensyms, and if they think this hack is good enough, I'll put it up on Anarki. Also, I hope pg reads this and either uses my fix (or fixes it himself), because I really would like working gensyms in the official distro. |