I've been watching the recent threads on special variables, top-level setq-s and the like, and I've come to the conclusion that the current situation is badly broken. I have a proposal on how to fix it that I'd like some feedback on.
The intractable problem is that beginners are chronically confused by the fact that Lisp does not make an adequate syntactic distinction between two completely different ways of binding variables. Imagine you are reading some code and you encounter the following two functions:
(defun foo (x) (blarg x))
(defun baz (y) (blarg y))
Do FOO and BAZ do the same thing? Well, they might or they might not. It depends. It depends on whether X or Y have been DEFVARed. It depends on whether there are any free references to X or Y in any function called by BLARG. It depends on whether you are trying to write thread-safe code. It depends on whether performance differences matter. It depends on whether your code is interpreted or compiled. And if you want to explain the reasons it depends on all these things you have to start talking about the differences between symbols and variables, what bindings are, and that there's nothing at all special about "special" variables, it's just an ancient piece of Lisp terminological baggage that means nothing more than "dynamic scope."
A beginner's initiation into this state of affairs invariably comes when he or she one day as an expedient types (defvar x 1) and then spends the rest of the day (or week) trying to figure out why her code is suddenly exhibiting mysterious bugs. The admonition to always use *...* notation for DAFVARed variables is a temporary fix, but it generally takes a very long time for a typical programmer to wrap their brains around what is really going on.
The problem is pervasive and subtle. Consider:
(funcall (lambda (x y) (lambda () ... x ... y ...)) 1 2)
Does this return a lexical closure over X and Y? Well, it might, or it might not. It depends.
Now, this has not been much of an issue because artificially separating the name spaces of lexical and dynamic variables using the *...* convention generally works to keep the problem at bay. But the trouble is that this solution only works if you use it, and you can only use it if you are aware of it. And even if you are aware of it, your collaborator might not be. *You* might not have DEFVARed X, but how do you know that Joe didn't?
The solution to this problem is to change the language so that the distinction between dynamic and lexical bindings is locally manifested. This is technically simple, but politically complex because it means either adding some kind of lexical declaration or getting rid of pervasive SPECIAL declarations (e.g. getting rid of DEFVAR, (PROCLAIM (SPECIAL ...)) etc.) My personal preference is the latter. Top-level declarations that radically change the semantics of code in ways that are not lexically apparent are EVIL!
Even local special declarations are IMO evil. The semantics of a piece of code ought not to depend on forward references. It's also very much against the spirit of a declaration. Declarations are for providing extra information for the compiler. They are not for changing the semantics of the program. It should be possible to remove all declarations without changing anything about the program except how fast it runs.
The solution is obvious. Special variables are nothing more than the symbol-value slot of some symbol. Why not make that simple fact manifest itself in the syntax? For example:
(let ( (x 1) ; A lexical binding ((symbol-value x) 2) ) ; A dynamic binding
While we're at it we could fold in the binding of multiple values and destructing into one uniform syntax:
(superlet ( (x 1) ; A lexical binding (guaranteed) ((symbol-value x) 2) ; == (let ((x 2)) (declare (special x)) ((values a b c) (foo)) ; == (multiple-value-bind (a b c) (foo) ((values a (symbol-value b) c) (baz)) ; As above, but with (declare (special b)) ((a b c) (foo)) ) ; A DESTRUCTING-BIND
A simple reader macro helps make things a lot less wordy:
$x == (symbol-value 'x)
So now we can write:
(let ( (x 1) ($y 2) ) ; X is a lexical binding, Y is a dynamic binding
This would work at top-level too:
(setf $x 1) ; Set the global X with no warnings and no semantic ambiguity (setf x 1) --> Warning! No lexical variable named X visible here. Setting $X instead.
To implement this proposal completely requires the cooperation of Lisp implementors because you have to change the way lambda lists are processed. But once that's done you can write (lambda (x $y) ...) instead of (lambda (x y) (declare (special y)) ...), or (multiple-value-bind (x $y) (foo) ...) instead of (multiple-value-bind (x y) (foo) (declare (special y)) ...) or (defun foo (x $y) ...) instead of (defun foo (x y) (declare (special y)) ...)
As a prototype I have implemented a macro called BIND that works as described above. BIND binds lexical and dynamic variables, and handles multiple values and destructuring. It also uses a paren-less LOOP-like syntax. I present it as both an illustration of how simple my proposal would be to implement properly, and as an illustration of how easy it is to change Lisp completely within the standard.
The syntax for BIND is:
(BIND var [=] value [and] ... in &rest body)
var : symbol | (symbol-value symbol) | (values var var ...) | list
Keywords in square brackets are optional.
Example: (BIND x = 1 and (z ($y) q) = (foo) and (values $a b c) = (baz) in ...)
expands to:
(let ( (x 1) ) (destructuring-bind (z (y) q) (foo) (declare (special y)) (multiple-value-bind (a b c) (baz) (declare (special a)) ...
I conjecture that beginners at least will find BIND easier to deal with than the things it replaces.
* Erann Gat | *You* might not have DEFVARed X, but how do you know that Joe didn't?
it seems to me that this the fundamental question. the answer lies in the implementation's excellence in implementing the function `describe' and any other environment-querying functions.
however, there _is_ something we can and should do: add explicit support for retrieving this important piece of information about a symbol, apart from the rather obvious user interface issues like querying the system when at a symbol name in Emacs. the compiler and the interpreter could also be asked to produce warnings about special variables for those who need them. (I'd favor declaring variables special locally as a nice way to document the known special effects and also silence such warnings.)
removing special variables because they confuse a few people is a typical "modern" reaction to the lack of diligence and effort that "modern" users are no longer expected to expend in learning anything. this is simply a beginner's issue. Common Lisp used to cater to experienced programmers at the cost of having to learn it, like a _skill_, something people of reasonable competence levels would _value_. such is of course terribly politically incorrect in this day and age, where blathering idiots get to vote as many times as they can by virtue of forgetting the question and anyone with any experience at all is considered prejudiced by virtue of not answering all questions up for vote with a blank stare.
I vote that Common Lisp remain a language that needs to be learned and studied, and instead focus our attention on stuff that actually affects users of all categories much more than this trifling issue, like being compatible with the notion in languages with which we would like to communicate of what constitutes a symbol name: the actual, literal sequence of characters (dollar signs included), not some case-mangled version of same.
I also vote that somebody write "the complete idiot's guide to special variables" instead of proposing silly language changes.
| $x == (symbol-value 'x)
I think "Common Perl" would be a good name for your modified language, with syntax in macros and equal signs and all. yuck.
As changing the language is a long term project at best, it really would be helpful if somebody wrote a guide mentioned in another response (though I should choose a different title :) .) I must admit that I for example do not understand these things well. Now it is easy to expect others to do something for you but on the other hand this should have to be done by someone who is an expert. And it should be written in a simple language (as simple as possible) with all terms carefully defined and with a *lot* of examples.
Janos Blazi
Erann Gat <g...@jpl.nasa.gov> schrieb in im Newsbeitrag: gat-2902001653240...@milo.jpl.nasa.gov...
> I've been watching the recent threads on special variables, top-level > setq-s and the like, and I've come to the conclusion that the current > situation is badly broken. I have a proposal on how to fix it that I'd > like some feedback on.
> The intractable problem is that beginners are chronically confused by > the fact that Lisp does not make an adequate syntactic distinction > between two completely different ways of binding variables. Imagine > you are reading some code and you encounter the following two functions:
> (defun foo (x) (blarg x))
> (defun baz (y) (blarg y))
> Do FOO and BAZ do the same thing? Well, they might or they might not. > It depends. It depends on whether X or Y have been DEFVARed. It depends > on whether there are any free references to X or Y in any function called > by BLARG. It depends on whether you are trying to write thread-safe code. > It depends on whether performance differences matter. It depends on whether > your code is interpreted or compiled. And if you want to explain the > reasons it depends on all these things you have to start talking about > the differences between symbols and variables, what bindings are, and > that there's nothing at all special about "special" variables, it's just > an ancient piece of Lisp terminological baggage that means nothing more > than "dynamic scope."
> A beginner's initiation into this state of affairs invariably comes when > he or she one day as an expedient types (defvar x 1) and then spends the > rest of the day (or week) trying to figure out why her code is suddenly > exhibiting mysterious bugs. The admonition to always use *...* notation > for DAFVARed variables is a temporary fix, but it generally takes a very > long time for a typical programmer to wrap their brains around what is > really going on.
> The problem is pervasive and subtle. Consider:
> (funcall (lambda (x y) (lambda () ... x ... y ...)) 1 2)
> Does this return a lexical closure over X and Y? Well, it might, or it > might not. It depends.
> Now, this has not been much of an issue because artificially separating > the name spaces of lexical and dynamic variables using the *...* convention > generally works to keep the problem at bay. But the trouble is that this > solution only works if you use it, and you can only use it if you are > aware of it. And even if you are aware of it, your collaborator might > not be. *You* might not have DEFVARed X, but how do you know that Joe > didn't?
> The solution to this problem is to change the language so that the > distinction between dynamic and lexical bindings is locally manifested. > This is technically simple, but politically complex because it means > either adding some kind of lexical declaration or getting rid of pervasive > SPECIAL declarations (e.g. getting rid of DEFVAR, (PROCLAIM (SPECIAL ...)) > etc.) My personal preference is the latter. Top-level declarations that > radically change the semantics of code in ways that are not lexically > apparent are EVIL!
> Even local special declarations are IMO evil. The semantics of a piece > of code ought not to depend on forward references. It's also very much > against the spirit of a declaration. Declarations are for providing extra > information for the compiler. They are not for changing the semantics of > the program. It should be possible to remove all declarations without > changing anything about the program except how fast it runs.
> The solution is obvious. Special variables are nothing more than the > symbol-value slot of some symbol. Why not make that simple fact manifest > itself in the syntax? For example:
> (let ( (x 1) ; A lexical binding > ((symbol-value x) 2) ) ; A dynamic binding
> While we're at it we could fold in the binding of multiple values and > destructing into one uniform syntax:
> (superlet ( (x 1) ; A lexical binding (guaranteed) > ((symbol-value x) 2) ; == (let ((x 2)) (declare (special x)) > ((values a b c) (foo)) ; == (multiple-value-bind (a b c) (foo) > ((values a (symbol-value b) c) (baz)) > ; As above, but with (declare (special b)) > ((a b c) (foo)) ) ; A DESTRUCTING-BIND
> A simple reader macro helps make things a lot less wordy:
> $x == (symbol-value 'x)
> So now we can write:
> (let ( (x 1) ($y 2) ) ; X is a lexical binding, Y is a dynamic binding
> This would work at top-level too:
> (setf $x 1) ; Set the global X with no warnings and no semantic ambiguity > (setf x 1) --> Warning! No lexical variable named X visible here. Setting > $X instead.
> To implement this proposal completely requires the cooperation of Lisp > implementors because you have to change the way lambda lists are > processed. But once that's done you can write (lambda (x $y) ...) > instead of (lambda (x y) (declare (special y)) ...), or > (multiple-value-bind (x $y) (foo) ...) instead of (multiple-value-bind > (x y) (foo) (declare (special y)) ...) or (defun foo (x $y) ...) instead > of (defun foo (x y) (declare (special y)) ...)
> As a prototype I have implemented a macro called BIND that works as > described above. BIND binds lexical and dynamic variables, and handles > multiple values and destructuring. It also uses a paren-less LOOP-like > syntax. I present it as both an illustration of how simple my proposal > would be to implement properly, and as an illustration of how easy it is > to change Lisp completely within the standard.
> The syntax for BIND is:
> (BIND var [=] value [and] ... in &rest body)
> var : symbol | (symbol-value symbol) | (values var var ...) | list
> Keywords in square brackets are optional.
> Example: (BIND x = 1 and (z ($y) q) = (foo) and (values $a b c) = (baz) in ...)
> expands to:
> (let ( (x 1) ) > (destructuring-bind (z (y) q) (foo) > (declare (special y)) > (multiple-value-bind (a b c) (baz) > (declare (special a)) > ...
> I conjecture that beginners at least will find BIND easier to deal with > than the things it replaces.
> (defun foo () > (declare (special x y)) > (list x y $z $w)) ; Look Ma, no warnings!
> (setf x nil y nil z nil w nil)
> ; Sample output: > ? (foo) > (NIL NIL NIL NIL) > ? (bind x = 1 in (list x (foo))) > (1 (NIL NIL NIL NIL)) > ? (bind $x = 1 in (list x (foo))) > (1 (1 NIL NIL NIL)) > ? (bind $x = 1 x = 2 in (list x (foo))) > (2 (1 NIL NIL NIL)) > ? (bind (x $y z $w) '(1 2 3 4) in (list x y z w (foo))) ; destrucuring-bind > (1 2 3 4 (NIL 2 NIL 4)) > ? (bind (values x $y z $w) (baz) in (list x y z w (foo))) ; multiple-value-bind > (1 2 3 4 (NIL 2 NIL 4)) > ? > |#
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =----- http://www.newsfeeds.com - The #1 Newsgroup Service in the World! -----== Over 80,000 Newsgroups - 16 Different Servers! =-----
In article <gat-2902001653240...@milo.jpl.nasa.gov>, g...@jpl.nasa.gov (Erann Gat) wrote: > The problem is pervasive and subtle. Consider:
> (funcall (lambda (x y) (lambda () ... x ... y ...)) 1 2)
> Does this return a lexical closure over X and Y? Well, it might, or it > might not. It depends.
It depends... but have you ever worried about x minght be special when you write the code like above? The chance are so remote that I think trying to eliminate it (by changing CL) is overkill.
> Now, this has not been much of an issue because artificially separating > the name spaces of lexical and dynamic variables using the *...* convention > generally works to keep the problem at bay. But the trouble is that this > solution only works if you use it, and you can only use it if you are > aware of it. And even if you are aware of it, your collaborator might > not be.
You can't force anyone not to write bad code by language spec alone. (In this case, however, you can search DEFVAR form in the code?)
**
I don't think this is not a CL specific problem. And programmers have already been advised to avoid the usage of 'global' variables with indefinite scope unless it is necessary.
Is there anything particularly dangerous when a beginner equates CL's special variables to global vars in other languages?
* Keke Abe | Is there anything particularly dangerous when a beginner equates | CL's special variables to global vars in other languages?
I can't answer for Erann, but my take on this is that beginners who get confused about this will remain confused for a few days, and then get it or get over it, as in: not worrying about it even if they don't get it.
if we change the semantics of the language from what said confused people will find described in textbooks and other reference materials and when searching the net, the number of days of confusion can only increase, not the least because half the vendors will think this is a lame idea and not implement it, and the other half will do it better than the lame code and so the only thing we will succeed in is in destroying a very powerful mechanism in Common Lisp that every other language is sadly lacking: transparent, safe, and convenient global, dynamic variables. all for the purported, but obviously unrealizable benefit of reducing the number of confused people and their posting frequency to comp.lang.lisp.
still, it would be nice if we had some simple programmatic access to the specialness of a symbol. this would have been covered by the environment access functions that were not included in the standard.
Erik wrote: > ...but my take on this is that beginners who get confused about this will > remain confused for a few days, and then get it or get over it, as in: not > worrying about it even if they don't get it.
As a beginner, this is my experience. I'm in the not worrying about it (or is that denial ;) stage. I found that this is one of those things that when I read the description of special vs dynamic vs local caused my head to hurt, but in practice has caused me little pain [1].
[...elided excellent reasons for not changing the sematics of the language...]
> still, it would be nice if we had some simple programmatic access to the > specialness of a symbol. this would have been covered by the environment > access functions that were not included in the standard.
This IMHO is a good idea.
Best Regards,
:) will
[1] Except in the case when I was writing some macros with cmucl. But this maybe more of an issue with the implementation declaring all top level variables special. More and better descriptions of this are elsewhere.
I think I agree with Erik about this. There is clearly a problem here in principle. In practice I have *very* seldom been bitten by it, and I'd be loth to change the language to make it `easier' for people at the cost of adding something like the BIND macro you suggest (at least, not with the syntax you suggest, which I have much the same reaction to as Erik I'm afraid). I'm also very loth to use up one of the extremely small number of available characters to be a read-macro for something like this.
I'd also like to gratuitously point out that your technique doesn't actually solve the whole problem: symbol-macros can mean that what looks like an innocent variable is actually something entirely different.
I think a much more useful meta-solution to this problem is better environmental information, and a compiler MOP and/or code walker. Given that I can instrument the compiler in such a way that it will warn me if it ever sees a special variable that does not adhere to whatever naming convention I choose, or in general check any other thing I like about the code I'm compiling.
And this solution has the advantage that it's useful for other stuff too!
g...@jpl.nasa.gov (Erann Gat) writes: > Now, this has not been much of an issue because artificially separating > the name spaces of lexical and dynamic variables using the *...* convention > generally works to keep the problem at bay. But the trouble is that this > solution only works if you use it, and you can only use it if you are > aware of it. And even if you are aware of it, your collaborator might > not be. *You* might not have DEFVARed X, but how do you know that Joe > didn't?
I didn't! Honest!
> The solution to this problem is to change the language so that the > distinction between dynamic and lexical bindings is locally manifested.
I agree that the distinction sohuld be locally manifest, but I don't think we need a radical change to the language to ensure this.
A little help from the programming environment would go a long way to solving the problem. Wrapping '*' around a variable is the de-facto standard for special variables. If the compiler issued a warning when you declared or used a special variable without '*', or if you declared or used a non-special (lexical) variable _with_ a '*', it would probably solve the bulk of the problem without introducing changes to the language.
>>>> In message <3160886469599...@naggum.no> >>>> On the subject of "Re: A modest proposal (long)" >>>> Sent on 01 Mar 2000 08:01:09 +0000 >>>> Honorable Erik Naggum <e...@naggum.no> writes:
>> >> still, it would be nice if we had some simple programmatic access >> to the specialness of a symbol.
in CLISP: (defvar x) (system::special-variable-p 'x) ==> T
> I think I agree with Erik about this. There is clearly a problem here > in principle. In practice I have *very* seldom been bitten by it, and > I'd be loth to change the language to make it `easier' for people at > the cost of adding something like the BIND macro you suggest (at > least, not with the syntax you suggest, which I have much the same > reaction to as Erik I'm afraid). I'm also very loth to use up one of > the extremely small number of available characters to be a read-macro > for something like this.
I have to conditionally agree with Erik (someone stop me, Please!!) if their experience is representative of general programing experiences. Structure changes in well established languages should only be made based as hard of data as possible. Before you change a basic feature like this, make sure the problem exists for programers or program teams who have at least 2-3 months of real experience ( not classes, or individual projects). For problems that mostly only effect beginners, more environmental information is both easier to implement and serves an educational purpose. I personnely(based on C, C++ and assembly lang exp) would find a describe function that would provide an complete description of the scope and type of a variable at the CURRENT time to be more useful. It would also be very nice, if the development system would list all of the variables visible at a given level. While I find *name* notation a little strange, hopefully there are not too many development teams of any size that do not have enforced naming standards for all variables and functions.
BTW does anyone know of a reference that has a GOOD description of hopefully general methods to come up with a naming standard for symbols, variables, functions etc.? Is there something approaching a standard.
My best coding experience was one in C, where the name of a variable or function told you in what file it was define, whether it was global, or a constant. It didn't include type, but that wasn't too much of a problem since almost every thing was an Integer, and on the DSP we were using short, int, long integer were all 32 bits and strings only existed in 2 out of 200 files.
> I think a much more useful meta-solution to this problem is better > environmental information, and a compiler MOP and/or code walker. > Given that I can instrument the compiler in such a way that it will > warn me if it ever sees a special variable that does not adhere to > whatever naming convention I choose, or in general check any other > thing I like about the code I'm compiling.
> And this solution has the advantage that it's useful for other stuff > too!
Robert Posey <mu...@raytheon.com> writes: > BTW does anyone > know of a reference that has a GOOD description of hopefully > general methods to come up with a naming standard for symbols, > variables, functions etc.? Is there something approaching a standard.
I don't think there are general standards, apart from:
- Use * to surround global special variables - Use + to surround global constants - Let yourself be guided by the (newer parts of the) ANSI CL standard for ideas on how to name accessors, functions and variables.
> My best coding experience was one in C, where the name of a variable or > function told you in what file it was define, whether it was global, > or a constant. It didn't include type, but that wasn't too much of > a problem since almost every thing was an Integer, and on the > DSP we were using short, int, long integer were all 32 bits and > strings only existed in 2 out of 200 files.
Well, since every useful (Lisp) development environment will include functions to locate the corresponding definition, encoding filenames in the names isn't very useful nowadays. Since in CL objects are typed and (normally) not variables, it doesn't make much sense to include type information in the name either.
Regs, Pierre.
-- Pierre Mai <p...@acm.org> PGP and GPG keys at your nearest Keyserver "One smaller motivation which, in part, stems from altruism is Microsoft- bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
> Well, since every useful (Lisp) development environment will include > functions to locate the corresponding definition, encoding filenames > in the names isn't very useful nowadays. Since in CL objects are > typed and (normally) not variables, it doesn't make much sense to > include type information in the name either.
That's if you are reviewing the code using the development system, that is often not the case where I work, or I suspect on many large development teams.
> -- > Pierre Mai <p...@acm.org> PGP and GPG keys at your nearest Keyserver > "One smaller motivation which, in part, stems from altruism is Microsoft- > bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
In article <3160886469599...@naggum.no>, Erik Naggum <e...@naggum.no> wrote:
> * Keke Abe > | Is there anything particularly dangerous when a beginner equates > | CL's special variables to global vars in other languages?
> I can't answer for Erann, but my take on this is that > beginners who get confused about this will remain confused for > a few days, and then get it or get over it, as in: not worrying > about it even if they don't get it.
It's not just a beginner problem. The program (DEFUN FOO (X) (BAR X)) (DEFVAR X) means something different from (DEFVAR X) (DEFUN FOO (X) (BAR X)) And if the DEFVAR is in one file, and the DEFUN is in another, then the semantics change silently depending on what order you compile them in. You wrote
> transparent, safe, and convenient global, dynamic variables
but I'd say this is a design flaw that makes them not particularly safe.
I don't think it's a bad enough problem to justify changing the language to solve it, but it's a real problem, even for real, experienced programmers. I recently finished renaming most of the special variables in the SBCL implementation of Common Lisp to use the *FOO* convention. It was a considerable amount of work. And even for the variables which use this convention, I still have to worry about subtly breaking something a la (DEFVAR *FOO*) (DEFUN BAR (..) (LET ((*FOO* (..))) (BLETCH *FOO*) (ZUT) ;; Don't move the DEFVAR to here, or you will lose. (DEFUN ZUT () ; which I hope isn't something obscene in Italian:-) (IF *FOO* (ZUT1) (ZUT2))) when *FOO*, BAR, and ZUT are defined in different files and the order of compilation might be rearranged. I'd need to be fairly unlucky to rearrange just the right things in just the right way to silently break something like this, but it could happen.
By and large, Common Lisp does a good job of letting you define things in any order, which is a good thing. By and large, when you can't define things in any order (e.g. DEFCLASS) at least it gives you an error, which is a good thing. But the problem I described just above won't give you an error in any implementation I'm aware of, which is nasty.
(By the way, I've considered making SBCL issue STYLE-WARNINGs for any SPECIAL use of non-*FOO*-style symbols, and any non-SPECIAL use of *FOO*-style symbols, to detect problems like this. But I'm a little uncomfortable embedding informal naming conventions in the compiler, so I've avoided doing this so far. Does anyone have any opinions on whether such STYLE-WARNINGs would be The Right Thing?)
> still, it would be nice if we had some simple programmatic > access to the specialness of a symbol. this would have been > covered by the environment access functions that were not > included in the standard.
Yes, I also really wish there was a standard way to query "is symbol FOO special?" And "what's the value of the optimization property BAR?" too..
Robert Posey <mu...@raytheon.com> writes: > > Well, since every useful (Lisp) development environment will include > > functions to locate the corresponding definition, encoding filenames > > in the names isn't very useful nowadays. Since in CL objects are > > typed and (normally) not variables, it doesn't make much sense to > > include type information in the name either.
> That's if you are reviewing the code using the development system, that > is often not the case where I work, or I suspect on many large development > teams.
How do you review the code? On printed paper?
Regs, Pierre.
-- Pierre Mai <p...@acm.org> PGP and GPG keys at your nearest Keyserver "One smaller motivation which, in part, stems from altruism is Microsoft- bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
In article <ey3ln42j056....@cley.com>, Tim Bradshaw <t...@cley.com> wrote: > I think I agree with Erik about this. There is clearly a problem here > in principle. In practice I have *very* seldom been bitten by it, and > I'd be loth to change the language to make it `easier' for people at > the cost of adding something like the BIND macro you suggest (at > least, not with the syntax you suggest, which I have much the same > reaction to as Erik I'm afraid). I'm also very loth to use up one of > the extremely small number of available characters to be a read-macro > for something like this.
I see I have not made myself clear.
The $ macro is optional. So is the BIND macro. The core of my proposal is to move the specification of dynamic bindings out of the declarations and into the lambda list. It's a 100% backwards- compatible change, and it doesn't change the semantics of the language at all. It's also very much in the spirit of Common Lisp. All I am saying is that we should extend lambda-list syntax to allow variables whose names are lists beginning with SYMBOL-VALUE (or SPECIAL or DYNAMIC, I don't really care which keyword we choose). It's much like extending the syntax of function names to include lists whose first element is SETF.
BTW, all the keywords in the BIND macro are optional except IN, so you can dispense with all the = signs and ANDs and just write:
(bind x 1 (symbol-value q) 2 (values y z) (foo) in ...)
It's also pretty simple to change BIND to use parens instead of a keyword to separate the bindings from the body:
(bind ( (x 1) ((symbol-value q) 2) ; implied (declare (special q)) ((values y z) (foo)) ; multiple-value-bind ((a (symbol-value b) c) (baz)) ) ; destructuring (with a special) ...
Without BIND this code becomes:
(let ( (x 1) (q 2) ) (declare (special q)) (multple-value-bind (y z) (foo) (destructuring-bind (a b c) (baz) (declare (special b))
Personally, I think:
(bind x 1 $q 2 (values y z) (foo) (a $b c) (baz) in ...
is a win. Notice also that you can implement this without a reader macro. BIND could simply declare special those variables whose print names start with $, or whose print names start and end with *, thus:
(bind ( (x 1) (*y* 2) ) ; *Y* is automatically declared special
We are already using a typographical convention to distinguish lexical and special variables. We already know that we get into trouble when we don't adhere to that convention. Why not move the burden of applying that convention from the programmer to the compiler?
I also wonder how many of you BIND haters are also LOOP haters. I am really beginning to worry that people have lost sight of the fact that an S-expression with just one level of parens is still an S-expression.
> I'd also like to gratuitously point out that your technique doesn't > actually solve the whole problem: symbol-macros can mean that what > looks like an innocent variable is actually something entirely > different.
But symbol macros can only be established with lexical scope, so if there is a symbol macro it must be lexically manifested. Global symbol macros would be truly problematic. In fact, this is the problem with DEFVAR. It essentially establishes a global symbol macro that replaces all occurrences of X with (symbol-value 'x), except that this happens silently, and there's no way to undo it.
* Erann Gat wrote: > I see I have not made myself clear.
No, I understood what you meant. I just want to know if this is a problem *in practice*[1], and if it is to think out a fix. Common Lisp is a pragmatic language and I don't think that changes to fix things that aren't in practice an issue are needed. That's why we don't have hygienic macros, after all.
I also think that if you are going to make a change to the language like this, you should think it out a good deal first. For instance my suggestion of better environmental access would enable you to detect these kinds of problems yourself, but it would also enable a whole bunch of other cool stuff.
If you want to stick to souping up lambda lists, why just fix this one issue? Why not let general declarations be in there? Step back from the problem.
> The $ macro is optional. So is the BIND macro. The core of my > proposal is to move the specification of dynamic bindings out of > the declarations and into the lambda list. It's a 100% backwards- > compatible change, and it doesn't change the semantics of the language > at all.
But, if it becomes standard, it *does* make every current conforming implementation non-conforming, and incurs a cost on every vendor who wishes to remain conforming. That is another reason to consider changes really carefully, and, I think, another reason to make changes that add as much value as possible rather than ad-hoc fixes to individual perceived problems.
> But symbol macros can only be established with lexical scope, so if > there is a symbol macro it must be lexically manifested. Global symbol > macros would be truly problematic. In fact, this is the problem with > DEFVAR. It essentially establishes a global symbol macro that replaces > all occurrences of X with (symbol-value 'x), except that this happens > silently, and there's no way to undo it.
DEFINE-SYMBOL-MACRO establishes global symbol macros. They are shadowed by bindings however.
--tim
[1] All I can say is that, for me, this has not been a problem, and people I've taught and worked with have not reported it as a problem. But I can't speak for any very wide community.
> > > Well, since every useful (Lisp) development environment will include > > > functions to locate the corresponding definition, encoding filenames > > > in the names isn't very useful nowadays. Since in CL objects are > > > typed and (normally) not variables, it doesn't make much sense to > > > include type information in the name either.
> > That's if you are reviewing the code using the development system, that > > is often not the case where I work, or I suspect on many large development > > teams.
> How do you review the code? On printed paper?
Usually, its sent as a text file and I review it as a separate file on a PC, in a code highlighting editor. So there is no way for the system to know data from other files. This is partly a result of using Ada, and old very limited tools. Since we are switching to C/C++, hopefully we will start using smart tools. So I have zero experience reviewing other people's code, using reasonable tools. My LISP coding experience is so far simply class work or playing around, and is pretty limited at the moment. Another problem is that many programs have classified code on them, which means that some small part of the code is classified. What I work on is not classified, so I often have limited access. They are starting to use Green hill compilers and Vxworks so I hope they will support better collaboration, but I wouldn't be surprised to see massive resistance to using an advanced features. However, I still think that you are going to run into problems if you don't use structured names, it seems so much easier to read the code that way. Its a pain to always have to click on every symbol to find out what it really means.
> -- > Pierre Mai <p...@acm.org> PGP and GPG keys at your nearest Keyserver > "One smaller motivation which, in part, stems from altruism is Microsoft- > bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
I see no evidence of that. I think we have a fairly good understanding of your issue. all the silly syntax detracted from its delivery, but you've been clear enough.
| We are already using a typographical convention to distinguish lexical | and special variables. We already know that we get into trouble when we | don't adhere to that convention. Why not move the burden of applying | that convention from the programmer to the compiler?
because we would want to make our own conventions in code where it is safe to make them. and people don't _actually_ get into trouble in the first place, except the first few days they are confused about this.
| I also wonder how many of you BIND haters are also LOOP haters. I am | really beginning to worry that people have lost sight of the fact that | an S-expression with just one level of parens is still an S-expression.
no need to worry about that. I like loop, I don't if*, and I don't like your bind or any of the numerous other _gratuitous_ syntax-heavy ideas people seem to get with an alarming regularity whenever they see a need for some miniscule improvement to the language. it's as if they don't like simple syntax to begin with, and rush to solve any semantic issue with syntax. I find this disturbing, but nonetheless indicative of something much more important: the language changers don't really grok Common Lisp.
| But symbol macros can only be established with lexical scope, so if | there is a symbol macro it must be lexically manifested. Global symbol | macros would be truly problematic.
take a look at define-symbol-macro some day and weep, then. I love the fact that we have global symbol macros! I also love the fact that we have constants, which also causes pervasive differences in behavior, and there's no truly established convention for them. I happen to think that special variables is one of Common Lisp's truly great idea. what I want, in response to your "it's EVIL!" is a programmatic means to query the system for the status of a symbol. `describe' and friends help me as a user who gawks at the screen, but that is clearly insufficient.
again, there is a need to change the language to make it more amenable to beginners and experienced users alike: unlike all other languages now in current and widespread use, Common Lisp violates the notion that what you see is what you get with respect to the _names_ of the symbols. if symbol-value is such a problem for beginners that it needs language-smith attention, wouldn't you be interested in solving a _real_ problem that would have far-reaching consequences for our interoperability with other languages, other textbooks, other people? it's not a question of case, it's a matter of making (setf (readtable-case *readtable*) :preserve) work the way people _actually_ expect it to. think about it. please.
* Bill Newman | It's not just a beginner problem. The program | (DEFUN FOO (X) (BAR X)) | (DEFVAR X) | means something different from | (DEFVAR X) | (DEFUN FOO (X) (BAR X)) | And if the DEFVAR is in one file, and the DEFUN is in another, then the | semantics change silently depending on what order you compile them in.
I maintain that this is a beginner problem, only. real Lisp programmers don't call their global variables "X". real Lisp programmers use packages if they want their symbols to stay of other people's face. real Lisp programmers know about unintern, too.
| I'd say this is a design flaw that makes them not particularly safe.
it's a design flaw to you because your notion of safe is wrong.
| (By the way, I've considered making SBCL issue STYLE-WARNINGs for any | SPECIAL use of non-*FOO*-style symbols, and any non-SPECIAL use of | *FOO*-style symbols, to detect problems like this. But I'm a little | uncomfortable embedding informal naming conventions in the compiler, so | I've avoided doing this so far. Does anyone have any opinions on whether | such STYLE-WARNINGs would be The Right Thing?)
I have already said what I think is the right thing here: demand that there be lexically apparent declarations that reiterate the special status of symbols so declared globally. lacking such a declaration, you might issue a style-warning for free variables even if you know they are globally declared special. it will lead to slightly more verbose code, but the excuse to be making invisible, pervasive changes would go away.
| Yes, I also really wish there was a standard way to query "is symbol FOO | special?" And "what's the value of the optimization property BAR?" too..
precisely, and this is the _only_ problem worth solving as I see it.
In article <3160942638240...@naggum.no>, Erik Naggum <e...@naggum.no> wrote: > | But symbol macros can only be established with lexical scope, so if > | there is a symbol macro it must be lexically manifested. Global symbol > | macros would be truly problematic.
> take a look at define-symbol-macro some day and weep, then.
You're right. I overlooked define-symbol-macro. I'm not weeping, but I am shuddering.
> I love the fact that we have global symbol macros!
What do you use them for?
> I happen to think that > special variables is one of Common Lisp's truly great idea.
I agree. The problem is not special variables. The problem is the way you tell Lisp which variables you want to make special and which you don't.
> what I want, > in response to your "it's EVIL!" is a programmatic means to query the > system for the status of a symbol. `describe' and friends help me as a > user who gawks at the screen, but that is clearly insufficient.
The issue is not just one of querying. It's also one of control. There's no way to undo the effects of a defvar short of uninterning the symbol.
> again, there is a need to change the language to make it more amenable to > beginners and experienced users alike: unlike all other languages now in > current and widespread use, Common Lisp violates the notion that what you > see is what you get with respect to the _names_ of the symbols.
Maybe it would help to review the distinction between a variable and a symbol (and the name of a symbol) just to make sure we are all on the same page. Variables, symbols, and names are all different. For example, here we have three (lexical) variables, but only two symbols, and only one name:
(lambda (x) (lambda (x) (lambda (foo::x) ...
(Actually, we have three variables ONLY if X has not been DEFVARed. If X has been defvared then we only have two variables.)
When you bind a lexical variable you make a new variable. When you bind a special/dynamic variable you make a new binding for an existing variable.
Specialness can be associated with variables (via SPECIAL declarations) or with symbols (via DEFVAR) but NOT with names. Once specialness has been associated with a symbol, it is no longer possible to create new variables using that symbol, only new bindings for the (one) special variable associated with that symbol (also known as the symbol's symbol-value).
The point is that none of this has anything to do with symbol names. In fact, you can make examples using symbols that don't even have names:
> if symbol-value is such a problem for beginners that it needs language-smith > attention, wouldn't you be interested in solving a _real_ problem that > would have far-reaching consequences for our interoperability with other > languages, other textbooks, other people?
I thought that's what I was doing, but it seems I still have not made myself clear. You seem to think that I am saying that the problem is the existence of dynamic variables and symbol-value. I'm not saying that at all. What I am saying is that the way things currently stand, when you write 'X' you can't in general know whether what you've written is a reference to a stack frame or a slot in an object on the heap. And, in fact, as you yourself pointed out the meaning of X can change over time if you are running interpreted. IMO that's bad.
> it's not a question of case, > it's a matter of making (setf (readtable-case *readtable*) :preserve) > work the way people _actually_ expect it to. think about it. please.
It would help if you would stop talking in riddles. I honestly have no idea what you mean.
> * Bill Newman > | It's not just a beginner problem. The program > | (DEFUN FOO (X) (BAR X)) > | (DEFVAR X) > | means something different from > | (DEFVAR X) > | (DEFUN FOO (X) (BAR X)) > | And if the DEFVAR is in one file, and the DEFUN is in another, then the > | semantics change silently depending on what order you compile them in.
> I maintain that this is a beginner problem, only. real Lisp > programmers don't call their global variables "X". real Lisp > programmers use packages if they want their symbols to stay > of other people's face. real Lisp programmers know about > unintern, too.
I don't think I'm going to be able to convince you of matters of taste here. (I've never seen anyone else do it.:-) But perhaps I can at least illustrate that there are more things in heaven and earth than are dreamt of in your philosophy. (Or, in software engineering as in quantum mechanics, the universe is not only stranger than we imagine, it is stranger than we can imagine.)
The programmers who wrote the Python compiler (part of CMU CL, available at http://www.cons.org/cmucl/) weren't beginners. But they put somewhat over 10K lines of Common Lisp into the "C" package (the compiler). And they defined special variables with names like C::TOP-LEVEL-LAMBDA-MAX C::NO-LOADS C::PACK-OPTIMIZE-SAVES C::PACK-SAVE-ONCE C::MAX-OPTIMIZE-ITERATIONS C::PACK-ASSIGN-COSTS C::VM-SUPPORT-ROUTINES C::PRIMITIVE-TYPE-SLOT-ALIST C::NO-COSTS C::SYSTEM-CONSTANTS I'm not trying to claim this was good style, or a good idea. (As I said in my earlier article, I've since renamed as many special variables as possible in the *FOO* style.) All I'm trying to claim is that this example shows that the problem is not confined to beginners. It was created by experienced programmers, and it bites experienced programmers now who have to worry about not naming local variables NO-LOADS. And even now that the special variables have been renamed in the *FOO* style in my version (SBCL), I still need to worry about respecting constraints on the order of compilation of code, with no help from the compiler even to tell me when something goes wrong.
Incidentally, experienced C and C++ programmers can avoid many of the problems of C and C++, such as * using the preprocessor to define constants * having to manually free memory resources * having wild pointers be able to corrupt the system But that because these problems mostly trouble beginners (or maintainers of large programs written by others) doesn't mean that these problems aren't shortcomings of C and C++. Why should the fact that the special variable problem mostly troubles beginners (or maintainers of large programs written by others) mean that this problem isn't a shortcoming of Common Lisp?
* wnewman wrote: > Incidentally, experienced C and C++ programmers can avoid many of the > problems of C and C++, such as > * using the preprocessor to define constants > * having to manually free memory resources > * having wild pointers be able to corrupt the system
Most of the experienced C and C++ programmers I know spend most of their time avoiding the latter two and/or buy very expensive tools (purify) to help them. Most of them are *very* troubled by these issues!
There's a real difference between problems which just go away, and problems that you eventually realise you can live with, but will just cost you 30% of your time for ever.
The CMUCL thing is a fine example of the former. Whoever did that was being silly, and there's a once off fix involving DO-SYMBOLS and tags-query-replace in emacs which really should not take that long.
* wnew...@my-deja.com | All I'm trying to claim is that this example shows that the problem is | not confined to beginners.
but your example doesn't show any such thing! that people are not following the asterisk convention is _not_ the problem. the problem as stated is that there's a lambda list or let binding somewhere with a symbol in it that has special binding which comes as a _surprise_ to people who are moderately (but not overly) intimate with the code. do you have evidence of that, or is this only more unfounded fear that there _might_ be a problem? incidentally, I recognize that there _might_ be a problem, the solution to which is to make the system easier to query for such information. I don't see any other problems that need solving.
my philosophy, in case you need to have it stated to avoid speculating about it, is that in order to serve the needs of any community, one must never, _ever_ to cater to the needs of ignorants and novices except in carefully controlled settings where the express purpose is to make them non-ignorants and non-novices, such as school or training courses.
g...@jpl.nasa.gov (Erann Gat) writes: > In article <ey3ln42j056....@cley.com>, Tim Bradshaw <t...@cley.com> wrote:
> > I think I agree with Erik about this. There is clearly a problem here > > in principle. In practice I have *very* seldom been bitten by it, and > > I'd be loth to change the language to make it `easier' for people at > > the cost of adding something like the BIND macro you suggest (at > > least, not with the syntax you suggest, which I have much the same > > reaction to as Erik I'm afraid). I'm also very loth to use up one of > > the extremely small number of available characters to be a read-macro > > for something like this.
> I see I have not made myself clear.
> The $ macro is optional.
If it's optional, ISTM it doesn't solve the problem (such as the problem is). Using *...* is also optional. So is using (declare (special ...)) when it's not strictly needed.
> Personally, I think:
> (bind x 1 $q 2 (values y z) (foo) (a $b c) (baz) in ...
> is a win.
[]
> I also wonder how many of you BIND haters are also LOOP haters. I am > really beginning to worry that people have lost sight of the fact that > an S-expression with just one level of parens is still an S-expression.
Well, we have a real serious disagreement there. I don't think you have the feel for sexps. Sticking parens around something may technically make it a sexp, but it doesn't make it a tolerable one. A sexp should not have internal dependencies. IMO even boa parameter lists are iffy.
> > I'd also like to gratuitously point out that your technique doesn't > > actually solve the whole problem: symbol-macros can mean that what > > looks like an innocent variable is actually something entirely > > different.
> But symbol macros can only be established with lexical scope, so if > there is a symbol macro it must be lexically manifested. Global symbol > macros would be truly problematic. In fact, this is the problem with > DEFVAR. It essentially establishes a global symbol macro that replaces > all occurrences of X with (symbol-value 'x), except that this happens > silently, and there's no way to undo it.
Instead of all this, how about a system parameter that makes even defvar require (declare (special ...)) or it's an error?