Hi,
I've been doing more thinking about Franz's whole case thing, and
about the different proposed solutions that have been suggested. But
I've noticed yet another problem with Franz's way of seeing things.
In the file $ACL_HOME/doc/cl/case.htm
you'll see the following advice:
============
5.3 Creating symbols
The most common non-portable form we come across looks like this
(defun constructor (struct) (intern (format nil "MAKE-~a" struct)))
This assumes that this code will run in Upper Case mode. Writing it as this allows the
case of the "make-" to be determined by the case of the :make- symbol's printname, which
will be correct for the case mode:
(defun constructor (struct) (intern (format nil "~a~a" :make- struct)))
============
The problem I see with this is that you're depending on *print-case*.
In fact, if *print-case* is :lower, which I like it to be (especially
since it prints out symbols in the CL package in lower-case, which are
then read back into upper-case when readtable-case is set to :invert),
then Franz's whole portability thing is broken, isn't it?
dave
From: Tim Bradshaw
Subject: Re: a problem with Franz's case suggestions
Date:
Message-ID: <nkju29bkgyk.fsf@tfeb.org>
David Bakhash <·····@alum.mit.edu> writes:
> The problem I see with this is that you're depending on *print-case*.
> In fact, if *print-case* is :lower, which I like it to be (especially
> since it prints out symbols in the CL package in lower-case, which are
> then read back into upper-case when readtable-case is set to :invert),
> then Franz's whole portability thing is broken, isn't it?
>
If that's their suggestion, then yes it is. In fact precisely this
kind of thing is exactly the sort of lossage I've found in various
programs which never thought about *print-case*.
I think to be really *sure* you should wrap your code in
(with-standard-io-syntax ...). What I tend to end up doing is
(format nil "~A" (symbol-name '#:foo))
which is not quite so good.
I think what I actually ended up doing was something like this:
(defun symbolify (&rest things)
(intern (apply #'concatenate 'string
(mapcar #'(lambda (x)
(etypecase x
(string x)
(symbol (symbol-name x))))
things))))
but this doesn't do everything, because you can't choose the package
easily. I like it though because it doesn't go anywhere near the
printer, which seems like better to me.
--tim
In article <··············@cadet.dsl.speakeasy.net>, ·····@alum.mit.edu says...
> The problem I see with this is that you're depending on *print-case*.
> In fact, if *print-case* is :lower,
Ok, assume that when this function is run, *print-case* is lower
and go back to the original function in the code:
(defun constructor (struct) (intern (format nil "MAKE-~a" struct)))
what will the name that this function creates look like?
Won't it be something like MAKE-foo ?
Isn't the code surrounding this function likely to have bound
*print-case* to :upcase just so that doesn't happen?
So in practice what we've suggested isn't a problem but of course
it can't hurt to bind *print-case* around format call just
to be sure (or to use (symbol-name :make-) to get *print-case*
out of it entirely).
John Foderaro <···@unspamx.franz.com> writes:
> In article <··············@cadet.dsl.speakeasy.net>, ·····@alum.mit.edu says...
> > The problem I see with this is that you're depending on *print-case*.
> > In fact, if *print-case* is :lower,
>
> Ok, assume that when this function is run, *print-case* is lower
> and go back to the original function in the code:
>
> (defun constructor (struct) (intern (format nil "MAKE-~a" struct)))
>
>
> what will the name that this function creates look like?
>
> Won't it be something like MAKE-foo ?
>
> Isn't the code surrounding this function likely to have bound
> *print-case* to :upcase just so that doesn't happen?
>
> So in practice what we've suggested isn't a problem but of course
> it can't hurt to bind *print-case* around format call just
> to be sure (or to use (symbol-name :make-) to get *print-case*
> out of it entirely).
Except that you end up polluting the KEYWORDS package.
Cheers
--
Marco Antoniotti =============================================================
NYU Bioinformatics Group tel. +1 - 212 - 998 3488
719 Broadway 12th Floor fax +1 - 212 - 995 4122
New York, NY 10003, USA http://galt.mrl.nyu.edu/valis
Like DNA, such a language [Lisp] does not go out of style.
Paul Graham, ANSI Common Lisp
From: Erik Naggum
Subject: Re: a problem with Franz's case suggestions
Date:
Message-ID: <3183166506151217@naggum.net>
* John Foderaro <···@unspamx.franz.com>
| So in practice what we've suggested isn't a problem but of course
| it can't hurt to bind *print-case* around format call just
| to be sure (or to use (symbol-name :make-) to get *print-case*
| out of it entirely).
You're big on "no problem in practice", but I am _very_ negative to
that general attitude towards solving problems. I want correctness
and adherence to specifications, not some justification that most of
the time, most of the carefully worked-out specification does not
matter, while the safe, general case applies. There are a number of
bugs in Allegro CL that have to be coded around by funny bindings and
fixes because you don't do your job correctly and according to the
full specification, but assume all sorts of simplifying short-cuts for
yourself. That's the New Jersey approach, not what we want in the
Common Lisp world.
Now, I'm not sure this is you only, but I have seen the code of many
engineers at Franz Inc, and I have the greatest respect for their
work, normally written with visible traces of painstaking attention to
detail. I have seen some more of your work and I am not consistently
impressed, to put it that way. Yes, it works, most of the time. Yes,
it does useful stuff, most of the time. Yes, it is speedily written
and satisfies that "we deliver" reuirement. But it is not _correct_.
Like, I have been asking for fixes to the code that reads and prints
symbols for a _very_ long time, only to find some fixes in ACL 6.0,
but this is _still_ broken:
(2) cl-user
*print-base*
=> 10
(3) cl-user
(let ((*print-base* 16)) (prin1 (list 'DEAD (intern "FACE"))))
(DEAD |FACE|)
=> (DEAD |FACE|)
It should print (|DEAD| |FACE|) and return (DEAD FACE) in Modern mode,
but it prints what it does because the first time the escaped print
name is cached, the base is honored, but it is not _stored_ with the
optimization, so when it is different, the cache is not invalidated.
You know about all this, John, since we have discussed it at great
length. I argued that until the caching was fixed, it should not be
enabled and that keeping the base and other parameters with the cache
was crucial to _correctness_. The _impression_ I got was that this
was not veryimportant because "in practice" people didn't fiddle with
these special variables, but that only means it won't be a problem "in
practice" either because people who try to use Allegro CL will get
burned and never try to use the specification to the fullest. By a
careful process of introducing such bugs, you lower the value of the
standard as a tool for good programmers (never mind the mediocre ones
who epitomize the "in practice" excuse) as a repository of knowledge
about the language and a treasure of understanding what the language
_should_ be like. To imagine that you can destroy all that important
trust in a community reference simply by optimizing the printing of
symbols incorrectly! You'd have to be phenomenally careless about
other people's time, money, and other values _not_ to think about the
_consequences_ of making such an optimization.
So, rather than _defending_ the buggy code in your documentation, the
responsible thing to do is to admit to the bug and fix it in the next
release. I still get these really bad vibes from someone who relies
on other people to "remember" something that they shouldn't have to
know about, which is not part of the specification, such as which
assumptions somebody else made about what the "common crowd" would or
would not "normally" do, so the "most of the time" could apply. It
breaks the simple and elegant and attractive ideas of abstraction and
encapsulation, too, so we are left with a product that we have to know
in _much_ more detail than we would need to if you had just done it
right the first time.
Finally, I'd like to draw people's attention to what I wrote back on
2000-11-08 in <················@naggum.net>:
* John Foderaro
| If you want to see the "ferociously detailed specs", they are slightly
| more than one page of html. Don't blink or you might miss it.
|
| http://www.franz.com/support/documentation/6.0/doc/case.htm#portability-1
Your code examples break if *print-case* is not :upcase.
This is just too shoddy work to publish, John.
It gets worse the more you defend it.
#:Erik
--
ALGORITHM: a procedure for solving a mathematical problem in a finite
number of steps that frequently involves repetition of an operation.
ALGOREISM: a procedure for solving an electoral problem in a finite
number of steps that frequently involves repetition of an operation.
Erik Naggum <····@naggum.net> writes:
[...snip...]
> By a careful process of introducing such bugs, you lower the value
> of the standard as a tool for good programmers (never mind the
> mediocre ones who epitomize the "in practice" excuse)
No, mind us as well please. As a mediocre "in practice" programmer I'd
like to point out that our "in practice" guesstimates have a slightly
less likelihood of bombing if the base system and the libraries adhere
strictly to standards and documented behaviour. In fact, we have a way
of doing things that are "not nice", which quite often touches on the
dusty corners of any standard.
--
H�kon Alstadheim, Montreal, Quebec, Canada
John Foderaro <···@unspamx.franz.com> writes:
> In article <··············@cadet.dsl.speakeasy.net>, ·····@alum.mit.edu says...
> > The problem I see with this is that you're depending on *print-case*.
> > In fact, if *print-case* is :lower,
>
> Ok, assume that when this function is run, *print-case* is lower
> and go back to the original function in the code:
>
> (defun constructor (struct) (intern (format nil "MAKE-~a" struct)))
>
>
> what will the name that this function creates look like?
>
> Won't it be something like MAKE-foo ?
>
> Isn't the code surrounding this function likely to have bound
> *print-case* to :upcase just so that doesn't happen?
>
> So in practice what we've suggested isn't a problem but of course
> it can't hurt to bind *print-case* around format call just
> to be sure (or to use (symbol-name :make-) to get *print-case*
> out of it entirely).
John. I was merely taking code out of the sample in the doc file.
This is how I actually do it in my code:
(defun constructor (stuct)
(intern (concatenate 'string
"MAKE-"
(string-upcase (symbol-name struct)))))
This is because I _never_ intern symbols that are anything but
uppercase. Normally, I write in all lowercase, but it's irrelevant
because readtable-case is set to :upcase for me, nearly all the time,
except where I sometimes set it to :invert, which I find to be an
extremely useful setting that people don't seem to be taking advantage
of.
dave
David Bakhash <·····@alum.mit.edu> writes:
>
> (defun constructor (struct) (intern (format nil "~a~a" :make- struct)))
>
> ============
>
> The problem I see with this is that you're depending on *print-case*.
> In fact, if *print-case* is :lower, which I like it to be (especially
> since it prints out symbols in the CL package in lower-case, which are
> then read back into upper-case when readtable-case is set to :invert),
> then Franz's whole portability thing is broken, isn't it?
Actually, the way this should print according to the standard will be
determined by a combination of readtable-case and *print-case*. The
interaction is designed to ensure that if you print a symbol name with a
particular combination of readtable-case and *print-case*, using the
same readtable-case will mean you get the same symbol by reading the
output.
====================
22.1.3.3.2 Effect of Readtable Case on the Lisp Printer
When printer escaping is disabled, or the characters under consideration
are not already quoted specifically by single escape or multiple escape
syntax, the readtable case of the current readtable affects the way the
Lisp printer writes symbols in the following ways:
:upcase
When the readtable case is :upcase, uppercase characters are
printed in the case specified by *print-case*, and lowercase
characters are printed in their own case.
...
:invert
When the readtable case is :invert, the case of all alphabetic
characters in single case symbol names is inverted. Mixed-case
symbol names are printed as is.
====================
--
Thomas A. Russ, USC/Information Sciences Institute ···@isi.edu