Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Symbols in DrScheme - bug?

2 views
Skip to first unread message

Jens Axel Søgaard

unread,
Nov 4, 2001, 10:12:52 AM11/4/01
to
I just spent an embarrassing long time hunting a bug in program written for DrScheme.
The program is a little port of an L-System program, I made for Guile.

It turned out that the problem was that DrScheme is in case insensitive mode by default.
If I put it in case sensitive mode the program worked ok.

This fooled me completely. I thought programs working in sensitive mode would
work without problems, when run in insensitive mode.
(The other way around is another matter),


In my testing, I tried the first three several times. It was by accident/paranoia I tried the
last one which solved the mystery.

> 'f
f
> 'F
f
> (eq? 'F 'f)
#t
> (eq? (string->symbol "f") (string->symbol "F"))
#f

Now. This last one, is that a bug? Or is this really intended?

--
Jens Axel Søgaard
<http://www.jensaxel.dk>

Anton van Straaten

unread,
Nov 4, 2001, 12:51:07 PM11/4/01
to
>> (eq? (string->symbol "f") (string->symbol "F"))
>#f
>
>Now. This last one, is that a bug? Or is this really intended?

If it's a bug, it would be a bug in R5RS - see Sec. 6.3.3 Symbols, esp.
symbol->string / string->symbol.

A few quotes:

"It is guaranteed that any symbol that has been returned as part of a
literal expression, or read using the read procedure, and subsequently
written out using the write procedure, will read back in as the identical
symbol (in the sense of eqv?). The string->symbol procedure, however, can
create symbols for which this write/read invariance may not hold because
their names contain special characters or letters in the non-standard case.
See symbol->string."

From symbol->string:
"If the symbol was returned by string->symbol, the case of characters in the
string returned will be the same as the case in the string that was passed
to string->symbol."

From string->symbol:
"This procedure can create symbols with names containing special characters
or letters in the non-standard case, but it is usually a bad idea to create
such symbols because in some implementations of Scheme they cannot be read
as themselves." DrScheme doesn't suffer from this latter problem, but the
problem you encountered is related.

I assume the point is that even if *identifiers* are not case-sensitive in
the language, one still might wish to be able to deal with case-sensitive
symbols for other reasons.

Anton

Jens Axel Søgaard

unread,
Nov 4, 2001, 2:46:20 PM11/4/01
to

"Anton van Straaten" <an...@appsolutions.com> wrote in message news:fefF7.13627$S4.12...@newsread1.prod.itd.earthlink.net...

> >> (eq? (string->symbol "f") (string->symbol "F"))
> >#f
> >
> >Now. This last one, is that a bug? Or is this really intended?
>
> If it's a bug, it would be a bug in R5RS - see Sec. 6.3.3 Symbols, esp.
> symbol->string / string->symbol.

You are absolutely right. I should have looked there first.

> The string->symbol procedure, however, can
> create symbols for which this write/read invariance may not hold because
> their names contain special characters or letters in the non-standard case.

Which indirectly says that quoted symbols are converted to the standard case
(lower or upper depending upon the implementation).

I think I was too focused on the "symbols are the same if and only they are spelled the same way"-kind of thinking. It was plausible
to think that (string->symbol "F") and 'F are spelled the same way.

In hindsight I realize that this behaviour makes it possible to determine
equivalence of symbols using one simple pointer comparision.

It is still annoying though. I used this construct

(case command
((F !) (draw distance))
((G) (move distance))
((+) (begin (right (* turns angle)) (set! turns 1)))

and since case uses eqv?, I experienced that none of the cases where fulfilled,
where command command was the symbol F (originating from a string).

In a case clause, one has to use datums, so I can not repair my code
writing ((string->symbol "F") !).

I solved my problem converting all input strings to the standard case, before
doing anything else.

[ My first version actually used chars ( #\F ), but I decided my code looked
prettier using symbols ( F ). This was a bad decision. ]

--
Jens Axel Søgaard

design, v.:
What you regret not doing later on.


ol...@pobox.com

unread,
Nov 5, 2001, 6:03:53 PM11/5/01
to
"Jens Axel S?aard" <dk-vid...@jasoegaard.dk> wrote in message news:<3be59b69$0$772$edfa...@dspool01.news.tele.dk>...

> It is still annoying though. I used this construct
> (case command
> ((F !) (draw distance))
> ((G) (move distance))
> ((+) (begin (right (* turns angle)) (set! turns 1)))
> and since case uses eqv?, I experienced that none of the cases
> where fulfilled, where command command was the symbol F
>(originating from a string).
> In a case clause, one has to use datums, so I can not repair my code
> writing ((string->symbol "F") !).

And yet you can write _portable_ code that uses case-sensitive
symbols, regardless of your Scheme system's support for case-sensitive
symbols. You can use case-sensitive symbols even in special forms such
as case. There are only two things you need to do to get the above
benefit: encode case-sensitive symbols as '"SymBol" (that is, a quote
followed by the string that spells the symbol), and enclose such code
in a 'sensitize-case' macro:

(define-macro sensitize-case
(lambda body
(define (re-write body)
(cond
((vector? body)
(list->vector (re-write (vector->list body))))
((not (pair? body)) body)
((and (eq? 'quote (car body)) (pair? (cdr body))
(string? (cadr body)))
(string->symbol (cadr body)))
(else (cons (re-write (car body)) (re-write (cdr body))))))
(cons 'begin (re-write body))))

Example:
(sensitize-case
(let ((command (string->symbol "Go")))
(case command
(('"Go" !) (display "Went!"))
(('"Move") (display "Moved"))
(else (display "stuck!")))))

The code does the right thing on Gambit (a case-sensitive system) and
on SCM and MIT Scheme (case-insensitive systems).

SSAX btw uses such a trick throughout. Therefore it works on both
case-sensitive and case-insensitive (such as SCM and MIT Scheme)
systems.


A thread
http://groups.google.com/groups?q=g:thl603175914d&hl=en&selm=200103290030.QAA99292%40adric.cs.nps.navy.mil

http://groups.google.com/groups?hl=en&threadm=8766g7g6il.fsf%40app.dial.idiom.com

(in particular, an article by Al* Petrofsky, shows how to "write"
a sensitize-case macro using R5RS macro system).

I can't help but to point out an abuse of sensitize-case:

(sensitize-case
(define (foo)
(let (('"1" 5) ('"" 7))
(display (+ '"1" '"" '"1")))))

This example looks especially spectacular in MIT Scheme. If you enter
the above code and evaluate (foo), you'll see the result 17. You can
ask MIT Scheme to print out the body of foo. MIT Scheme is capable of
printing out a closure:

1 ]=> (pp foo)

(named-lambda (foo)
(let ((1 5) ( 7))
(display (+ 1 1))))

Rather remarkable piece of Scheme code: numbers and empty strings can
act as legitimate Scheme identifiers! I like (let ((1 5)) ...) the
most. What a nice illustration of a difference between notation and
denotation.

Jens Axel Søgaard

unread,
Nov 6, 2001, 9:39:25 PM11/6/01
to

<ol...@pobox.com> wrote in message news:7eb8ac3e.0111...@posting.google.com...

> "Jens Axel S?aard" <dk-vid...@jasoegaard.dk> wrote in message news:<3be59b69$0$772$edfa...@dspool01.news.tele.dk>...

> And yet you can write _portable_ code that uses case-sensitive


> symbols, regardless of your Scheme system's support for case-sensitive
> symbols.

I was worried about that.

> You can use case-sensitive symbols even in special forms such
> as case. There are only two things you need to do to get the above
> benefit: encode case-sensitive symbols as '"SymBol" (that is, a quote
> followed by the string that spells the symbol), and enclose such code
> in a 'sensitize-case' macro:
>
> (define-macro sensitize-case
> (lambda body
> (define (re-write body)
> (cond
> ((vector? body) (list->vector (re-write (vector->list body))))
> ((not (pair? body)) body)
> ((and (eq? 'quote (car body)) (pair? (cdr body))
> (string? (cadr body)))
> (string->symbol (cadr body)))
> (else (cons (re-write (car body)) (re-write (cdr body))))))
> (cons 'begin (re-write body))))
>
> Example:
> (sensitize-case
> (let ((command (string->symbol "Go")))
> (case command
> (('"Go" !) (display "Went!"))
> (('"Move") (display "Moved"))
> (else (display "stuck!")))))

Thanks for the macro. It made me think up another solution:

(define resymbolize
(if (eq? 'A (string->symbol "A"))
(lambda (s) (compose string->symbol string-upcase symbol->string))
(lambda (s) (compose string->symbol string-downcase symbol->string))))

Now I can write:

(define g (string->symbol "Go"))

(case (resymbolize g)
((Go) (display "Went!"))
(else (display "stuck")))

A better resymbolize, woud check that its argument indeed is a symbol,
and if not, behave as the identity.

The price I pay is that I loose the original casing of the symbol;
but for my use - dispatching only - it is ok.

> The code does the right thing on Gambit (a case-sensitive system) and
> on SCM and MIT Scheme (case-insensitive systems).
>
> SSAX btw uses such a trick throughout. Therefore it works on both
> case-sensitive and case-insensitive (such as SCM and MIT Scheme)
> systems.
>
>
> A thread
> http://groups.google.com/groups?q=g:thl603175914d&hl=en&selm=200103290030.QAA99292%40adric.cs.nps.navy.mil
>
> http://groups.google.com/groups?hl=en&threadm=8766g7g6il.fsf%40app.dial.idiom.com

Ah. Your and Petrofskys code solves the problem in generality. Quite nice.

> (in particular, an article by Al* Petrofsky, shows how to "write"
> a sensitize-case macro using R5RS macro system).
>
> I can't help but to point out an abuse of sensitize-case:
>
> (sensitize-case
> (define (foo)
> (let (('"1" 5) ('"" 7))
> (display (+ '"1" '"" '"1")))))
>
> This example looks especially spectacular in MIT Scheme. If you enter
> the above code and evaluate (foo), you'll see the result 17. You can
> ask MIT Scheme to print out the body of foo. MIT Scheme is capable of
> printing out a closure:
>
> 1 ]=> (pp foo)
>
> (named-lambda (foo)
> (let ((1 5) ( 7))
> (display (+ 1 1))))

Brilliant.

> Rather remarkable piece of Scheme code: numbers and empty strings can
> act as legitimate Scheme identifiers! I like (let ((1 5)) ...) the
> most. What a nice illustration of a difference between notation and
> denotation.

An example perfect for a book in denotational semantics.
Hm, people would proabably first appreciate the example after they read the book.

--
Jens Axel

0 new messages