Erik Naggum <e...@naggum.net> wrote: +--------------- | * Rainer Joswig | | Unless you create non-interned symbols. ;-) | | So complete the list of functions by adding make-symbol. Furrfu. +---------------
Now that you bring it up, here's a (style?) question I've been wanting to ask (still being somewhat only a casual user of CL):
Other than reducing possible confusion while debugging with "macroexpand", is there any real reason to prefer (gensym "foo") over (make-symbol "foo") when defining macros that need non-capturing local variables? Both produce fresh uninterned symbols which can't conflict with (capture) any other symbol. So in what circumstances is one preferred over the other, or vice-versa?
-Rob
p.s. I think I know why one doesn't use literal uninterned symbols for such things, e.g.:
You only get *one* uninterned symbol when the macro is defined, which gets used for all possible instances of the macro. So if you had nested occurrences, in the expansion an inner occurrence of #:tmp could shadow an outer one. But that can't happen with:
Erik Naggum <e...@naggum.net> writes: > Really? Assuming that we still talk about symbol-function and > symbol-value, the hysterical need for "hygienic" macros in Scheme is > _not_ caused by their conflated namespaces?
Please explain what you mean with that. Common Lisp has its separate namespaces, and even there macros commonly use gensym so as not to accidentally use variables of their callers. How does this differ from Scheme's hygienic macros?
(Did you mean that Scheme macros are _forced_ to be hygienic?)
* Simon Brooke wrote: > I do think that treating functions as different from other data is > 'stupid', yes; it leads to all sorts of hacks and kludges and results > in no benefits that I am aware of.
Do you see the same issue with the class namespace, the package namespace, or any of the other n namespaces that CL has?
| Common Lisp has its separate namespaces, and even there macros | commonly use gensym so as not to accidentally use variables of their | callers. How does this differ from Scheme's hygienic macros? (Did | you mean that Scheme macros are _forced_ to be hygienic?)
Yes, Scheme has a "high-level" macro mechanism in which hygiene is enforced. This is the only standardized macro mechanism in Scheme. Schemers have also realized that it is sometimes necessary to break hygiene intentionally, so there are various proposals to accomplish that. It's been too long since I kept track of the Scheme world; maybe one of these proposals is emerging as standard, or maybe not.
Scheme's hygienic macros are really easy to use, and ought to eliminate a whole host of bugs, at least in the hands of inexperienced programmers. I see no reason why they cannot be implemented in CL -- not replacing CL macros of course, but as a supplement. Does anybody know if this has been tried? Of course, if there is some deeper reasons why this cannot or should not be done, I'd be thrilled to hear about them.
-- * Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/> - "There arises from a bad and unapt formation of words a wonderful obstruction to the mind." - Francis Bacon
* Rob Warnock | Other than reducing possible confusion while debugging with | "macroexpand", is there any real reason to prefer (gensym "foo") | over (make-symbol "foo") when defining macros that need | non-capturing local variables? Both produce fresh uninterned symbols | which can't conflict with (capture) any other symbol. So in what | circumstances is one preferred over the other, or vice-versa?
Well, I use make-symbol exclusively and see no reason for gensym or gentemp at all. I tend to set *print-circle* to t, anyway.
#:Erik -- If this is not what you expected, please alter your expectations.
Centuries ago, Nostradamus foresaw a time when Erik Naggum would say:
>* Rob Warnock >| Other than reducing possible confusion while debugging with >| "macroexpand", is there any real reason to prefer (gensym "foo") >| over (make-symbol "foo") when defining macros that need >| non-capturing local variables? Both produce fresh uninterned symbols >| which can't conflict with (capture) any other symbol. So in what >| circumstances is one preferred over the other, or vice-versa?
> Well, I use make-symbol exclusively and see no reason for gensym or > gentemp at all. I tend to set *print-circle* to t, anyway.
Interesting.
That means that rather than macro-expanding into something that the system provides, you generate whatever symbol name you want, right? -- cbbro...@hex.net - <http://www.hex.net/~cbbrowne/> Rules of the Evil Overlord #157. "Whenever plans are drawn up that include a time-table, I'll post-date the completion 3 days after it's actually scheduled to occur and not worry too much if they get stolen." <http://www.eviloverlord.com/>
> > > Errr... this needs a bit of expansion. Symbols in Common LISP are > > > case-insensitive. Common LISP is the most prevelent LISP in use these > > > days, but that doesn't make it the only one. Symbols in InterLISP and > > > Portable Standard LISP, for example, are case sensitive.
> > Common Lisp symbols *are* case sensitive:
> > USER(1): (eq 'foo '|foo|) > > NIL
> > However, the CommonLisp reader may canonicalize the case of an > > unescaped symbol before interning it. (And the printer may > > canonicalize the case before printing it, as well.)
> Well, what also happens is that these symbols are INTERNed > in a package. Depending on what INTERNing does you can have > the same symbol or not. The behaviour of Common Lisp's > interning of symbols can be found in the standard docs.
Whether the symbols are interned or not makes no difference to case sensitivity. Two symbols that differ in case can *never* be EQ, regardless of whether they are interned or not. (This does *not* imply that two symbols that have the same symbol name *are* necessarily EQ.)
Simon Brooke <si...@jasmine.org.uk> writes: > I do think that treating functions as different from other data is > 'stupid', yes;
Separation of namespaces is not about treating functions different from other data. As data, CL treats functions identically to other data.
However, there can be no mistaking that most of everything that functions do is (a) access variables, and (b) call functions. As such, having special syntax for dealing with the "calling" of functions (nothing to do with the function datatype itself) seems appropriate, just as having special syntax for dealing with slot or array access in many languages is meaningful and appropriate. Long experience with natural languages shows that people prefer to take things that they use a lot and make shorthand notations for them. That's one reason people like to use macros a lot, btw, because it allows them to optimize the presentation of common idioms in a manner appropriate to their frequency of use.
> it leads to all sorts of hacks and kludges and results > in no benefits that I am aware of.
Hacks and kludges? I don't think so. You're welcome to cite some.
As to benefits, you need only look to the paper you cite below (for which I was a co-author and for which my contribution was to do the "benefits" part).
> It is, in my opinion, historical > baggage left over from implementation details in LISP > 1.5.
Nonsense. Taken on its face, this kind of conclusion would imply that left to start anew, with no constraints of compatibility, people would not make the same choice again. I and many others would readily make the same choice again and NUMEROUS popular languages do distinguish the namespaces of functions and variables, not just Lisp. For good reason: People's linguistic hardware is, as evidenced by the extremely complex mechanisms for context exhibited in human languages, well optimized for resolving names in different contexts with only the context as a cue. It would be a waste of already existing brain wetware, and a pessmimization of that wetware's function, to fail to take advantage of such context information.
> Furthermore, it's clear that many of the most influential people > in the design of Common LISP are now of the same opinion; for example, > Richard Gabriel's 1988 paper in _LISP & Symbolic Computation_ on just > this issue,
My paper, too. And no, it was not on the issue of why the separation is bad. It was on the PROS AND cons of the situation. That paper's direct result was that CL decided the weight of the evidence was technically in favor of keeping the status quo.
Among other things, the paper specifically cites a speed issue that is not easily resolvable without either two namespaces or an EXTREMELY POWERFUL inference engine--more powerful than most Scheme implementations promise. In particular, if you do (setq x #'foo) you don't know for sure that X is going to be used as a function. It might be it will just be used as data. And certainly if you do (setq x 3) you don't know that X is *not* going to be used as a function. As a consequence, when you call X in a Lisp1 (the term I coined in the paper you cite for the language subfamily that includes languages like Scheme which have only a single namespace), then you have to check at call time whether X contains something valid to execute as a function. The only case in which you can just immediately do a machine-level instruction jump to the contents of X is where you have done theorem proving enough to know that X contains good data. In the case of some functions like (define (foo x y) (x y)) this can be arbitrarily complicated to prove because it means knowing all callers of FOO, and since neither Scheme nor Lisp is strongly typed, you rapidly run up against the halting problem proving that (X Y) is a valid function call at compile time, leaving the call as inefficient as (defun foo (x y) (funcall x y)) in Common Lisp, except that in this case, Common Lisp calls attention to the fact that there is something unusual about the call, and some Common Lisp programmers appreciate that. Indeed, CL also provides the ability to declare that X is promised to be a function, consequently allowing an efficient compilation even where theorem proving in Scheme might fail (because Scheme designers, myself among them but outvoted) have stubbornly resisted providing a declaration mechanism in Scheme that would allow it to know what the programmer knows but what cannot easily be detected by general inferencing.
It's not just the FUNCALL case, though; in general, in Scheme, any name whose value is ever assigned free is at risk of being a slow call unless that call can be demonstrated to always get safe values. And yet, Scheme can't compile in safety checks at the time of assignment because it doesn't know you will do a call to the function during the time with that bad value. Consider: (block (set! x 3) (print (+ x 1)) (set! x car) (print (x '(a b c)))) Here it requires a great deal of compiler smarts to jump directly to the function X efficiently because the compiler needs to be fearful that X will contain 3 at the time of jump. Only very careful flow analysis shows this to be safe, and it's more complicated if these two pairs of SET!/PRINT operations occur in unrelated parts of programs.
By contrast, the CL design means that when you do the assignment (which most people agree occurs statistically a LOT less often than the calling), you can do the test then. That is, (progn (setf (symbol-function 'x) 3) ; this can signal an error ...) As a consequence, all references to the symbol function namespace are safe, and as a consequence of that, all references of the form (f x) can be 100% sure that the function cell of f contains something that is truly safe to jump to. This is a big speed gain, and the speed gain is enjoyed regardless of whether the compiler has had a chance to completely analyze all of the flow paths, so there's no chance that a later addition of new code will violate this contract in being-debugged code.
> or some of Scott Fahlman's posts on LISP2 and the design > of Dylan on this very group in February and March of 1995.
I didn't read this so have no opinion.
> However, if you have a different opinion, please feel free to argue > it. What benefit is there, beyond having to invoke baroque syntax to > use a lambda expression, and having to do two operations instead of > one at every step in a structure walker?
There are several additional benefits.
One is that statistically, functions are used less often in CL. A number of people don't like "functional programming" as a style, or feel nervous about it, and want it "case marked" when it happens so they can pay attention closer. That benefit doesn't apply to all, but is a feature to those that it does apply to.
Another benefit is that it allows the correct spelling of variable names. I notice a lot of Scheme people who spell variables whose english name is "list" as LST just to avoid an accidental collision with the LIST operator. CL people don't worry about this. They write variables like LIST because they don't worry that (defun foo (list) (mapcar #'(lambda (x) (list 'foo x)) list)) will have some confusion about which list means what. Practical experience in CL says functions are almost always constant, and so (list x y) is reliable to write anywhere to mean "make a list of two things". And binding a lambda variable doesn't change that.
Another benefit is that when you see (list x y) on a screen of a definition that is not the start of definition, you don't have to scroll back in order to understand its meaning. It's true that someone could try to FLET LIST, but as a practical convention, good programmers just don't ever FLET or LABELS definitions that are globally defined. So when you see a function that looks like it's doing a certain function call, it generally is. Not so at all in Scheme.
It's well and good to say that you'd make this arbitrary choice differently if you were designing the language. But it's not well and good to say there are no reasons for the language being as it is, nor is it well and good to ascribe the reasons for the change as being "historical" and "ones that would obviously be made differently with hindsight". Many choices in the language are about either predicting a usage pattern, or about accomodating a usage pattern that will select a certain user group you desire to cater to. CL sought to cater to a set of people who are largely quite pleased with this decision and most of which would be gravely offended if you changed it. In that regard, it made the "right" decision. Not because "right" is a uniquely determined attribute of the world, but because "right" is a term meaningless without context. ... Uh, that is to say, I guess, that there are multiple namespaces in which the term "right" is assigned a value.
I'll close with a favorite quote of mine (wish I knew the source) and the obligatory analysis of the quote.
There are two kinds of people in the world. Those who think there are two kinds of people and those who don't.
The war about two namespaces isn't, I think, just about whether or not it's useful to sometimes distinguish context in functions and variables. In my opinion, it's about whether there is one and only one right way to think about programs. Most Lisp2 supporters don't object to the Lisp1 world existing; they see a multiplicity of options and are happy for the one they have. But Lisp1'ers tend to be annoyed at Lisp2 more often. And I think it's not just about namespaces. It's about the fact that other programmers have a choice of how to think, and that they can be happy in a world where the axioms are chosen differently. I think that's terribly sad.
We all benefit from multiple points of view. We are robbed when denied them. We should all be seeking
r...@rigden.engr.sgi.com (Rob Warnock) writes: > Other than reducing possible confusion while debugging with "macroexpand", > is there any real reason to prefer (gensym "foo") over (make-symbol "foo")
I'm reminded of the joke
"Other than that, Mrs. Lincoln, how did you like the play?"
This is, after all, hardly a minor issue. I think it's the key reason, since sometimes there may be several calls to the same macro in the same space yielding visually-otherwise-indistinguishable values that you couldn't sort out otherwise.
Of course, MAKE-SYMBOL might be chosen in some case where such debugging wasn't needed, or where it was known that only one such instance was to be made, or where non-program-readable visual brevity might be needed.
MAKE-SYMBOL can, of course, also be used in bootstrapping package configurations where you want to hold a symbol hostage for a little while before you INTERN it.
And, finally, MAKE-SYMBOL is subprimitive to GENSYM. You need it to implement GENSYM. So you might as well expose it just in case. In case someone wants to write a better GENSYM... as people often used to want back when GENSYM didn't have as many options. And even now sometimes people want to vary it...
* Kalle Olavi Niemitalo | Please explain what you mean with that.
How can it be any clearer? Scheme decided to conflate the function and variable namespaces, and approximately 50 milliseconds later, began whining about Common Lisp's lack of hygienic macros (Scheme people are always whining about something in Common Lisp), simply because a variable binding in Scheme screws up function calls, so an unintentional variable binding colliding with a local function will make Scheme code using non-hygienic macros very unsafe. Scheme code uses local functions a lot more than Common Lisp, so you'd think they'd realize the need to separate the namesaces is even stronger than in Common Lisp, but noooo, "separate namespaces is stupid" so they'd rather suffer the complicating consequences of their decision than fix it.
| Common Lisp has its separate namespaces, and even there macros | commonly use gensym so as not to accidentally use variables of their | callers.
That's _so_ irrelevant. Common Lisp macros don't use fresh symbols in order to avoid accidentally clobbering the functional value of the symbols it uses. Scheme's hysterical need for hygiene comes from the very significant danger of clobbering _functional_ values. (That's why you see Scheme code us silly variable names likt "lst" instead of "list", too -- in case you want to call `list', it'd better not be some arbitrary list of data.)
| How does this differ from Scheme's hygienic macros?
It doesn't, of course, but if you look only for similarities to what you already know, you will find very little of interest in your life.
Human beings don't have any problems with the noun "house" naming a very different concept than the _verb_ "house". Anyone who argued that "no, no, you can't call your building a `house' because that's already a verb" should just be shot to help him out of his misery.
#:Erik -- If this is not what you expected, please alter your expectations.
Rob Warnock wrote: > Other than reducing possible confusion while debugging with "macroexpand", > is there any real reason to prefer (gensym "foo") over (make-symbol "foo") > when defining macros that need non-capturing local variables? Both produce > fresh uninterned symbols which can't conflict with (capture) any other symbol.
You obviously realize that the additional behavior GENSYM provides is that each generated symbol is visibly different from other generated symbols. This doesn't matter to the compiler, evaluator, or other code walkers (which of course only depend on EQness of variable names) but is helpful to human readers of macroexpanded code.
If it weren't for this, the save-space-at-any-cost luddite faction would have invented some semistandard alternative to GENSYM that would give all generated symbols the same (as in EQ) null symbol name. One could still differentiate these using *PRINT-CIRCLE*, but this would hardly improve the readability of macroexpanded code.
GENSYM is a rather old function and I don't feel its behavior is optimal for its typical use. In particular, each time it is used for macroexpansion the names of the generated variables are numerically different. This means that when my program throws into the debugger and the stack backtrace shows gensyms, one cannot usefully macroexpand the source functions to determine which generated variable is which. This has slowed me down many times when debugging.
I wonder if some better conventions cuold be devised. Suppose the expansion of DEFMACRO wrapped the expander body in a MACROLET for smoe GENSYM-like operator that would communicate the nested origin of the macro. That way, each macroexpansion (relative to top level) would produce the "same" macroexpansion, where all uninterned variable names would always have a reproducible SYMBOL-NAME, even though the symbols themselves would remain distinct, as required by hygene. This would enhance readability abd debuggability of macro-generated code.
> Among other things, the paper specifically cites a speed issue that is not > easily resolvable without either two namespaces or an EXTREMELY POWERFUL > inference engine--more powerful than most Scheme implementations promise. > In particular, if you do > (setq x #'foo) > you don't know for sure that X is going to be used as a function. It might > be it will just be used as data. And certainly if you do > (setq x 3) > you don't know that X is *not* going to be used as a function. As a > consequence, when you call X in a Lisp1 (the term I coined in the paper > you cite for the language subfamily that includes languages like Scheme > which have only a single namespace), then you have to check at call time > whether X contains something valid to execute as a function. The only case > in which you can just immediately do a machine-level instruction jump to the > contents of X is where you have done theorem proving enough to know that X > contains good data. In the case of some functions like > (define (foo x y) (x y)) > this can be arbitrarily complicated to prove because it means knowing all > callers of FOO, and since neither Scheme nor Lisp is strongly typed, you > rapidly run up against the halting problem proving that (X Y) is a valid > function call at compile time, leaving the call as inefficient as > (defun foo (x y) (funcall x y)) > in Common Lisp, except that in this case, Common Lisp calls attention to the > fact that there is something unusual about the call, and some Common Lisp > programmers appreciate that. Indeed, CL also provides the ability to declare > that X is promised to be a function, consequently allowing an efficient > compilation even where theorem proving in Scheme might fail (because Scheme > designers, myself among them but outvoted) have stubbornly resisted providing > a declaration mechanism in Scheme that would allow it to know what the > programmer knows but what cannot easily be detected by general inferencing.
> It's not just the FUNCALL case, though; in general, in Scheme, any > name whose value is ever assigned free is at risk of being a slow call > unless that call can be demonstrated to always get safe values. And yet, > Scheme can't compile in safety checks at the time of assignment because it > doesn't know you will do a call to the function during the time with that > bad value. Consider: > (block > (set! x 3) > (print (+ x 1)) > (set! x car) > (print (x '(a b c)))) > Here it requires a great deal of compiler smarts to jump directly to the > function X efficiently because the compiler needs to be fearful that X will > contain 3 at the time of jump. Only very careful flow analysis shows this > to be safe, and it's more complicated if these two pairs of SET!/PRINT > operations occur in unrelated parts of programs.
You don't need two namespaces to handle this efficiently, you can do it with function cacheing the way MIT Scheme does it. When a free variable is first invoked as a function, the value is checked to make sure it is a function, and the entry point is computed. This entry point is placed in a cache in the caller. The next time the function is called, it is jumped to directly via the cache.
(Actually, the entry in the cache is *always* directly jumped to. To invalidate the cache, you replace the entry with a call to a trampoline that fetches the value, checks it, and updates the cache.)
> By contrast, the CL design means that when you do the assignment (which > most people agree occurs statistically a LOT less often than the calling), > you can do the test then.
The cacheing mechanism described above depends on this feature. When you assign a variable that contains a function, you must invalidate all cached links. You could be aggressive and relink at this point, but being lazy works just as well.
> There are several additional benefits.
> One is that statistically, functions are used less often in CL. A > number of people don't like "functional programming" as a style, or > feel nervous about it, and want it "case marked" when it happens so > they can pay attention closer. That benefit doesn't apply to all, but > is a feature to those that it does apply to.
I don't consider this a benefit. In fact, the special `case marked' syntax may make people feel that `something weird' is happening. If you are using a single namespace lisp, you could certainly add some syntax to mark the places you are invoking a computed function.
> Another benefit is that it allows the correct spelling of variable names. > I notice a lot of Scheme people who spell variables whose english name > is "list" as LST just to avoid an accidental collision with the LIST > operator. CL people don't worry about this. They write variables like LIST > because they don't worry that > (defun foo (list) > (mapcar #'(lambda (x) (list 'foo x)) > list)) > will have some confusion about which list means what. Practical experience > in CL says functions are almost always constant, and so (list x y) is reliable > to write anywhere to mean "make a list of two things". And binding a lambda > variable doesn't change that.
I agree that this is a benefit.
> Another benefit is that when you see (list x y) on a screen of a definition > that is not the start of definition, you don't have to scroll back in order > to understand its meaning. It's true that someone could try to FLET LIST, > but as a practical convention, good programmers just don't ever FLET or LABELS > definitions that are globally defined. So when you see a function that looks > like it's doing a certain function call, it generally is. Not so at all in > Scheme.
Not true. Good Scheme programmers don't shadow global variables any more capriciously than good lisp programmers.
> You can "INTERN" symbols. Interning symbols means that > you put them in a package.
No no no. Nein nein nein. Gubbish, rubbish, and false:
You cannot INTERN a SYMBOL. INTERN accepts only a STRING as the first argument. The result of calling INTERN of a STRING is either the SYMBOL previously present in the PACKAGE, or else a newly created SYMBOL INTERNed in the PACKAGE.
In normal usage it doesn't matter when one writes informally and inaccurately. But when investigating and explicating the semantics of the language, clarity and precision are paramount.
IMPORT and other operators may cause a SYMBOL to become present in a PACKAGE.
> You don't need two namespaces to handle this efficiently, you can do > it with function cacheing the way MIT Scheme does it. When a free > variable is first invoked as a function, the value is checked to make > sure it is a function, and the entry point is computed. This entry > point is placed in a cache in the caller. The next time the function > is called, it is jumped to directly via the cache.
> (Actually, the entry in the cache is *always* directly jumped to. > To invalidate the cache, you replace the entry with a call to a > trampoline that fetches the value, checks it, and updates the cache.) [...] > When > you assign a variable that contains a function, you must invalidate > all cached links. You could be aggressive and relink at this point, > but being lazy works just as well.
Caching, trampolines, relinks and lazy invalidation must make assignments and some function calls much slower. A similar, more complex mechanism is (or at least should be) in place for CLOS generic method call optimizations, but I think that's besides the point Kent was making.
Joe Marshall <jmarsh...@alum.mit.edu> wrote: +--------------- | Kent M Pitman <pit...@world.std.com> writes: | > but as a practical convention, good programmers just don't ever FLET | > or LABELS definitions that are globally defined. So when you see a | > function that looks like it's doing a certain function call, it generally | > is. Not so at all in Scheme. | | Not true. Good Scheme programmers don't shadow global variables any | more capriciously than good lisp programmers. +---------------
Joe, I really *like* Scheme, and I write much more of it than CL, but I have to agree with Kent (and from previous comments, Erik) on this one.
In Scheme you have to be a *lot* more careful to avoid shadowing global variables, because *all* of the standard functions are global "variables". Not only can one not safely use "list" as a local variable, you can't use "string" or "vector" or "min" or "max" -- or for that matter, "quotient", "remainder", "real-part", "magnitude" or "angle"!! A *lot* of names that are natural choices for intermediate-result are denied to the programmer, because Scheme, a Lisp1, chose to give builtin functions those names.
IMHO, the situation would be far worse did not Scheme use the "?" suffix convention for predicates and the "!" suffix for mutators. OTOH, that hack denies all such potential variable names to programmers as well, since one must assume that someday *somebody* might want to name a function with one of those.
-Rob
p.s. And while we're making comparisons... nah, never mind, don't get me started about how in Scheme "length" is not generic...
----- Rob Warnock, 41L-955 r...@sgi.com Applied Networking http://reality.sgi.com/rpw3/ Silicon Graphics, Inc. Phone: 650-933-1673 1600 Amphitheatre Pkwy. PP-ASEL-IA Mountain View, CA 94043
On 10 Jul 2000 04:04:06 GMT, r...@rigden.engr.sgi.com (Rob Warnock) wrote:
> IMHO, the situation would be far worse did not Scheme use the "?" suffix > convention for predicates and the "!" suffix for mutators. OTOH, that hack > denies all such potential variable names to programmers as well, since one > must assume that someday *somebody* might want to name a function with one > of those.
FTR Dylan (an OO Scheme) similarly has conventions for class names to avoid collisions in a Lisp-1:
define class <foo> ( <object> ) end class;
define function doit ( foo :: <foo> ) format-out( foo ); end;
And <foo> is a constant that evaluates to a class object. There is no find-class( #"<foo>" ).
Robert Monfera <monf...@fisec.com> writes: > Joe Marshall wrote: > [...] > > You don't need two namespaces to handle this efficiently, you can do > > it with function cacheing the way MIT Scheme does it. When a free > > variable is first invoked as a function, the value is checked to make > > sure it is a function, and the entry point is computed. This entry > > point is placed in a cache in the caller. The next time the function > > is called, it is jumped to directly via the cache.
> > (Actually, the entry in the cache is *always* directly jumped to. > > To invalidate the cache, you replace the entry with a call to a > > trampoline that fetches the value, checks it, and updates the cache.) > [...] > > When > > you assign a variable that contains a function, you must invalidate > > all cached links. You could be aggressive and relink at this point, > > but being lazy works just as well.
> Caching, trampolines, relinks and lazy invalidation must make > assignments and some function calls much slower.
Assignments are indeed slower, but note that Kent had this to say:
> By contrast, the CL design means that when you do the assignment (which > most people agree occurs statistically a LOT less often than the calling), > you can do the [functionp] test then.
He is proposing that when you assign to a function cell, you ensure that the cell *always* has a functional value (i.e., you can always jump to the entry point. Again, you would use a trampoline to indicate an unbound function cell, or a function cell bound to an object other than a function.)
r...@rigden.engr.sgi.com (Rob Warnock) writes: > Joe Marshall <jmarsh...@alum.mit.edu> wrote: > +--------------- > | Kent M Pitman <pit...@world.std.com> writes: > | > but as a practical convention, good programmers just don't ever FLET > | > or LABELS definitions that are globally defined. So when you see a > | > function that looks like it's doing a certain function call, it generally > | > is. Not so at all in Scheme. > | > | Not true. Good Scheme programmers don't shadow global variables any > | more capriciously than good lisp programmers. > +---------------
> Joe, I really *like* Scheme, and I write much more of it than CL, but I > have to agree with Kent (and from previous comments, Erik) on this one.
> In Scheme you have to be a *lot* more careful to avoid shadowing global > variables, because *all* of the standard functions are global "variables". > Not only can one not safely use "list" as a local variable, you can't use > "string" or "vector" or "min" or "max" -- or for that matter, "quotient", > "remainder", "real-part", "magnitude" or "angle"!! A *lot* of names that > are natural choices for intermediate-result are denied to the programmer, > because Scheme, a Lisp1, chose to give builtin functions those names.
I agree that there are too many symbols that have `unfortunate' names. But you don't find too many scheme programs that bind `vector-ref', `intern', `+', `expt', `sqrt', `cons-stream', `load', etc. etc.
I'm not sure that I think that having two namespaces is the appropriate solution, though.
In article <m28zvekqsf....@gododdin.internal.jasmine.org.uk>, si...@jasmine.org.uk says... much more experienced than me.
> Allegedly[1], part of the US DoD requirement was that Common LISP > should be usable with a type of terminal which had no shift key, and > could display only upper case. The DoD was very influential in the > beginnings of the Common LISP project.
No, this had nothing to do with the decision. When Common Lisp was being designed it was technically possible to make the case-sensitive and case-insensitive people happy. In fact the most widely distributed Lisp at the time could support both modes. You could write case- sensitive code and also load in Macsyma, a very large Lisp application written for a case insensitive Lisp (MacLisp). [by case sensitive support I mean that the reader has a mode where you can naturally program in case sensitive manner in your environment, and on Unix that means that you don't have to write everything in all caps, and that symbols names have normal behavior (i.e. the symbol foo has a print name of "foo").]
Allegro CL has always supported both modes, although it could have been done in a much nicer way if the support had been part of the Common Lisp spec. I tried to convince the committee that case sensitivity was important for Lisp as it was being used in case sensitive environment (e.g. the Unix operating systems where system library names are case sensitive). Some members didn't believe that it was possible to have a programming language with case sensitive identifiers (because you couldn't speak your programs), and others didn't want anyone to have the ability to write case sensitive lisp code for fear that they may one day have to read it [i'm serious, these are the actual arguments against support for a case sensitive lisp].
So rather than design a language that would have make everyone happy, the committee chose explicitly to exclude the case sensitive group. Since that time the case sensitive world has grown dramatically (C, C++, Perl, Java, xml) and interfacing with it critical for Lisp's survival (we Lisp programmers can't write everything, we have to link to code written by outsiders). I've been using a case sensitive lisp for 20 years now and I have to say that it makes interfacing with things in the Unix and Windows world much easier.
Joe Marshall <jmarsh...@alum.mit.edu> writes: > You don't need two namespaces to handle this efficiently, you can do > it with function cacheing the way MIT Scheme does it. > [non-controversial stuff about caching omitted]
I was comparing apples-to-apples an implementation with two namespaces and one without. Your reply says to me, in effect, that it's possible to build two namespaces and then to hide the fact. So much for functions not being "treated specially" and so much for a "kludge and hack free" system. But certainly I did not mean to imply there was no way to win; I was merely pointing out, as I said, that it's all trade-offs. In CL you get the speed for free in a straightforward way if you like 2 namespaces (and I do); in Scheme you don't get it for free because you bargained away the two namespaces for something you valued more, so you pay some other place. The world is full of options, and the key thing to understand is that this just emphasizes that the choice is arbitrary.
The key thing isn't that I don't need two namespaces. I WANT two namespaces. I want them for linguistic reasons alone. But you asserted there were no benefits and I pointed out that in addition to the linguistic reasons, there are efficiency benefits from having made the choice thusly.
> > There are several additional benefits.
> > One is that statistically, functions are used less often in CL. [...]
> I don't consider this a benefit. In fact, the special `case marked' > syntax may make people feel that `something weird' is happening. If > you are using a single namespace lisp, you could certainly add some > syntax to mark the places you are invoking a computed function.
I realize you don't consider this a benefit, but I want to emphasize that this does not mean it's not. No benefit is something everyone uses and so the test of benefitness is not "there exists a person who doesn't care"; rather, the test is "there exists a person who DOES care".
On the other hand, some features do work as misfeatures with some people or in some situations. I'd prefer to say "this is also a non-benefit". I regard making lists of "pros and cons" as mostly an exercise in monotonic logic--barring people being outright deceitful, you can't really refute what they have said by saying the opposite--you can just point out that the world is pretty complicated, logically speaking. And it is.
But I specifically want to negate the claim that "There are no benefits to a Lisp2" since that's what you had been saying. What I think you meant to say was "The benefits of a lisp2 don't speak to me." That's ok.
Btw, as an aside, I should point out that I coined the terms Lisp1 and Lisp2 when writing this paper with Gabriel, and I prefer it in all discussion of this kind over any use of "Scheme" or "CL". I found that some people have personally passionate feelings about CL and Scheme which have nothing to do with this argument, and I find it especially useful to sometimes think about a Lisp2 variant of Scheme and a Lisp1 variant of CL to keep the discussion honest and to keep people's feelings about unrelated issues from swaing things.
Oh, and one final note: I personally have long wanted to see a Lisp Omega in which there were an arbitrarily extensible number of namespaces and where you could define both Lisp and Scheme in terms of common operators. I once sketched out how such a system would work and partially implemented it but have never completed it. Sometime if I get time I will. The basic essence would be to have operators that take namespace keywords like (ASSIGN (VALUE CAR FUNCTION) (VALUE CDR VARIABLE)) or that take compound tokens as "variable" names like: (SET! (IDENTIFIER CAR FUNCTION) ...) In this way, programs written in one or the other language could interoperate by appealing to a more general substrate of lower-level interchange notations. I often wonder if the Lisp2 haters don't hate Lisp2 not for having multiple namespaces, but for having too few (that is, fewer than infinity). 2 has a certain arbitrariness that 1 and 0 seem not to. Though one is able to play fast and loose with which of 1 or 0 gets to be the non-arbitrary value of those two arbitrarily chosen non-arbitrary values. Heh... Anyway, it's on my list to finish up the implementation of this sometime and maybe make a conference paper out of it or something, but time is always so short...
> > You don't need two namespaces to handle this efficiently, you can do > > it with function cacheing the way MIT Scheme does it. > > [non-controversial stuff about caching omitted]
> I was comparing apples-to-apples an implementation with two namespaces > and one without.
I was taking issue with your statement that ``the speed issue is not easily resolvable without either two namespaces or an EXTREMELY POWERFUL inference engine.'' A relatively simple cacheing mechanism can also be used to speed up function calling.
> Your reply says to me, in effect, that it's possible > to build two namespaces and then to hide the fact.
I don't understand why you think that there are two namespaces. There is only one. What is done differently is that when a variable appears as the operator element of a combination, it is treated as a function. But you *have* to do this.
> So much for functions not being "treated specially" and so much for > a "kludge and hack free" system.
Functions are special in that any combination the leftmost element is applied to the remainder of the elements. I don't think cacheing the result of a variable lookup is a `kludge'.
> But certainly I did not mean to imply there was no way to win; > I was merely pointing out, as I said, that it's all trade-offs. In CL > you get the speed for free in a straightforward way if you like 2 namespaces > (and I do); in Scheme you don't get it for free because you bargained away > the two namespaces for something you valued more, so you pay some other place.
Yes. But you seemed to be asserting that there is an irreducable cost in terms of performance that can only be ameliorated by heroic efforts.
> But you asserted there were no benefits and I pointed out that in > addition to the linguistic reasons, there are efficiency benefits > from having made the choice thusly.
I don't believe that I asserted that there were no benefits. I asserted that a two namespace Lisp does not have an inherent performance advantage over a single namespace Lisp.
> > > There are several additional benefits.
> > > One is that statistically, functions are used less often in CL. [...]
> > I don't consider this a benefit. In fact, the special `case marked' > > syntax may make people feel that `something weird' is happening. If > > you are using a single namespace lisp, you could certainly add some > > syntax to mark the places you are invoking a computed function.
> I realize you don't consider this a benefit, but I want to emphasize that > this does not mean it's not. No benefit is something everyone uses and so > the test of benefitness is not "there exists a person who doesn't care"; > rather, the test is "there exists a person who DOES care".
I *do* care, actually, and I dislike having to use FUNCTION and FUNCALL to mark the code.
> On the other hand, some features do work as misfeatures with some people > or in some situations. I'd prefer to say "this is also a non-benefit". > I regard making lists of "pros and cons" as mostly an exercise in > monotonic logic--barring people being outright deceitful, you can't really > refute what they have said by saying the opposite--you can just point out > that the world is pretty complicated, logically speaking. And it is.
I think I made myself clear: I don't consider special marking of functions passed as values to be a benefit. If you wish to recast that statement as `Jrm does not appreciate the many benefits of special marking of functions passed as variables', feel free to read it that way.
> But I specifically want to negate the claim that "There are no benefits > to a Lisp2" since that's what you had been saying.
I never made that claim.
> What I think you meant to say was "The benefits of a lisp2 don't > speak to me." That's ok.