You are correct that it is effectively black magic, in that you probably don't need to know about it right now. Only a small percentage of Racket programmers will ever need to know about it.

When you look at the "reference/symbols.html" page of Racket documentation, pretend that you don't see anything other than "symbol?", "string->symbol", and "symbol->string". You will probably use those three procedures a lot, and you will probably never need to use anything else on that page.

I suggest making your own somewhat "gensym"-like procedure, tailored to your needs. If you're, say, generating symbols that must be unique only within a single generated Web page, write your own procedure to support doing exactly that. In this procedure, you will naturally use "string->symbol", and consequently procedures like "eq?", "memq", and "assq" will simply work like you'd expect (no black magic). As an example, here's one utility procedure you might write, which itself produces a "gensym"-like procedure that you can use in the context of, say, generating a single Web page:

;; Definition of your utility procedure:
(define (make-serially-numbered-symbol-maker prefix-string)
 (let ((serial-number 0))
   (lambda ()
     (let ((last-serial-number serial-number))
       (set! serial-number (+ 1 serial-number))
       (string->symbol (string-append prefix-string
(number->string last-serial-number)))))))

;; Example use of that utility procedure:
(let ((my-make-symbol (make-serially-numbered-symbol-maker "foo-")))
 (list (my-make-symbol)
       (my-make-symbol)
       (my-make-symbol)))
;;==> '(foo-0 foo-1 foo-2)

If a procedure that produces new procedures seems a little too fancy-pants right now, you can do a similar thing in a block of your code that needs the symbols made, without the part about producing a procedure. The nice thing here about a procedure that produces a new procedure is that it's a good way to put this code into a reusable library, rather merely having a code pattern that you retype or copy&paste every time you need to do a similar thing.

Esoterica #1: If you wanted the produced symbol maker (in the example above, a particular "my-make-symbol" value) to be callable from multiple threads simultaneously, then the code should be modified to use a semaphore, so that the setting of "last-serial-number" and the incrementing of "serial-number" happens in a mutex block. (The "string->symbol" part could be left out of the mutex block, to increase parallelism.) But you probably don't want to be calling the same "my-make-symbol" from multiple threads simultaneously, anyway, so I didn't complicate this illustration with a semaphore.

Esoterica #2: You'll see functions like "gensym" and "gentemp" in some old Lisp dialects, and they will be used mostly to get around problems of non-hygienic macros in that particular dialect. I don't recall seeing them used in real Racket or Scheme code, however, probably because those languages have hygienic macros.

Estoterica #3: In some Lisp dialects (not Racket), interned symbols are not garbage-collected, so, if you tried to generate unique symbols infinite numbers of times in a run of a program, *eventually* they might want to take up more space than can be represented by your computer and then by the universe.

--
http://www.neilvandyke.org/
_________________________________________________
 For list-related administrative tasks:
 http://lists.racket-lang.org/listinfo/users

Reply via email to