Up to now I had the impression that packages were like sharable namespaces -- contents protected from outside assault, unless explicit means (like IN-PACKAGE) were used to alter their content.
But I found that by simply using a package inside another client package, I could accidentally redefine an imported symbol for my own needs in the client package -- and this redefinition actually redefines the used symbol back in the original package -- without any warning!!
E.g.,
;; -------------------------------------- ;; in parent file...
(defpackage "THING" (:export "DOIT"))
(in-package "THING")
(defun doit () 15)
----------------------------------------- ;;; in client file
(defpackage "THING-USER" (:use "THING"))
(in-package "THING-USER")
(defun doit () 16)
;; -----------------------------------
Now if I go back to package "THING" and examine #'doit, I find that it has the definition given to it by the THING-USER package.
Now this seems a bit startling, especially since I received no warning about redefining the doit symbol. It would seem that barrel-loads of problems could occur downstream, as other unsuspecting user packages get their expected behaviors modified by some other unknowing client package.
I see in CLHS no mention of what the expected behavior is. In fact, the whole issue of package definitions seems still up in the air.
Is this particular situation implementation dependent? Or is it the expected behavior? And if so, shouldn't some kind of warning be issued? How is a client package ever to know that it might be redefining some exported symbol?
... Actually, I think that points to the danger inherent in just blindly USE'ing a package instead of making explicit imports. But then the base LISP package is a real pain to explicitly import all the things so commonly used. I think there is a protection mechanism to prevent its symbols from being redefined.
But this is a lesson to me -- beware of just blindly USE'ing other packages without fully realizing what they export.
"David McClain" <bar...@qwest.net> writes: > Up to now I had the impression that packages were like sharable > namespaces -- contents protected from outside assault, unless explicit means > (like IN-PACKAGE) were used to alter their content.
> But I found that by simply using a package inside another client package, I > could accidentally redefine an imported symbol for my own needs in the > client package -- and this redefinition actually redefines the used symbol > back in the original package -- without any warning!!
There are two issues here.
First, you should NEVER use IN-PACKAGE if you don't intend to be the implementor of the package you are using. So there is no bug there.
Second, I have long wished that compilers would issue a style diagnostic if you tried to define a variable or function from within a package that did not belong to that package. That would, of course, get you a lot of style warnings if you did:
(in-package "FOO")
(defun bar:x (x) ...)
but that's the point. Probably a mechanism could be created for muffling the warning if vendors experimented with this and negotiated with users about how to make it tolerable.
> Now this seems a bit startling, especially since I received no warning about > redefining the doit symbol.
As defined now, you have no right to a warning. What you're saying is the same as saying you have write access to someone's directory, you've read in their file and written it out incompatibly, and are surprised you clobbered their file. Think of an imported symbol like a hard link in a file system, if you are a unix user and have ever seen non-symbolic links in action.
> It would seem that barrel-loads of problems could occur downstream, as other > unsuspecting user packages get their expected behaviors modified by some > other unknowing client package.
Do not share symbols with people you don't want this to happen for. Nothing wrong with doing:
(defun foo (x) (other:foo x))
rather than importing the OTHER package.
> I see in CLHS no mention of what the expected behavior is. In fact, the > whole issue of package definitions seems still up in the air.
Sure you do. But CLHS is a specification, not a tutorial. This is all implicit in the extensive discussion of how all the various package operations for inheritance works, what it means for symbols to have the same identity, how symbol lookup works, etc. Once you have the same symbol in your hand, it doesn't matter how it got there.
This is also the reason that CLHS tells you that you must not redefine symbols in the CL package. Because to do so would break every other use in the system. You should apply for yourself the same kinds of rules as are outlined in 11.1.2.1.1 for *all* packages that you use or until you're more familiar with symbol sharing.
> Is this particular situation implementation dependent?
Absolutely not.
> Or is it the expected behavior?
Absolutely. It is at the heart of what it traditionally meant to be "object-oriented" until the term was recently (last decade or two) co-opted by people who decided to make object-oriented be a design methodology. The original meaning of object-oriented really focused on object identity--that the language and its data were about the identity of things. Packages allow you to select which identity the symbol FOO has, but once you have the object with that identity, there is no trace in the object itself of how you got to that object. Just as you can't tell whether the list (1 2) was originally typed with one space, as (1 2) or three like (1 3) or perhaps with intervening comments like (1 ;foo 2 ;bar ) because no trace of its input format is relevant, so too, you can't tell once you have the symbol in hand whether you wrote FOO:BAR or just BAR or what. Programs might have constructed the symbol and there might never have been a source form. Only the OBJECT matters, and that's what makes it OBJECT oriented. (Sorry, I'm not really yelling, just trying to make the emphasis on the right words in flat ASCII.)
> And if so, shouldn't some kind of warning be issued?
There are some reasons why it might be useful, as I mentioned above, but they would be heuristic in any case. There is ome reason to believe that compilers could do better than they're doing now, but in the sense of "shouldn't the compiler *know* what's going on" the answer is a definite no. The compiler can't "have to know" because to guarantee its ability to know is to require information that might in some cases never have been there, and that in any case would be counter to what is at least necessary and sometimes even desirable.
To put this in a way that might sound less negative, let me retell a story about a professor I really liked in school, the late Bill Martin, who taught a computational linguistics class I took. He said it this way: the purpose of language is not to say the things that are obvious, but to say the things that are not. If all we ever wanted to say were the simple things, there would be no syntax. We would simply say "cheese, mouse, eat" and everyone would know what was up. Language syntax exists NOT for the purpose then of saying "The mouse ate the cheese" but for saying "The cheese ate the mouse". It is in order that we be able to be surprised that we give ourselves the ability to construct things beyond the obvious.
So when you encounter thing that surprise you, please do not always get out your gun and shoot at them just because people in other language-design villages find them good sport. Sometimes they are there for a reason.
The world is FILLED with languages that warn you about everything that seems even a little out of the ordinary, and one reason we always say that Lisp is a language about doing what you've been told is impossible is not that we are miracle workers who can do the literally impossible, but rather that other languages are quick to label as impossible that which is not impossible. They so limit their world view by making one fear certain things that things start to seem impossible that are not.
In the US, it's nearly impossible to raise a kid any more without a HUGE amount of income because one can't do things you can still, I think, do in some other countries like "leave the kid alone for five minutes while you have to be away". I'm sure if the kid dies, you'd be criminally responsible here. In other countries, people would say "it's too bad the kid died". You might look at that and say "of course we should protect our children" but you also have to look at the infinite expenditure of resources that goes into making sure there is absolutely no chance that any risk will ever befall a child. The cost of insuring that steals other resources from raising children, and is not zero cost. It also means you raise kids to think someone will always protect them instead of raising kids to look out for themselves. So it's very double-edged.
And languages are just like that. Many other languages protect you from what you might write, and you're talking about an area of CL where it doesn't protect you.
There are places CL does protect you that it might not. For example, it warns you about unused lambda variables, even though it's useful sometimes to ignore them. It's common place in the functional programming community to write functions like (lambda (x y) x) and its an annoyance to them to be warned about this bug. We had to make an engineering choice in CL of whether to support that style (quietly compiling that which might be a bug or might be a useful style). We decided it doesn't happen often enough that we didn't want to warn, but we created the IGNORE and IGNORABLE declarations to let you do that if you wanted. (lambda (x y) (declare (ignore y)) x). But by doing so, we not only observe statistics, we create them. It makes it less likely that people will use this style. I don't happen to mind that, but others might. There is no way to win totally. Each decision favors some group. The same is true of the symbol identity thing.
> How is a client package ever to know that it might be redefining > some exported symbol?
Well, good implementations will tell you when you're redefining a function in a file that it wasn't originally defined in. That's an optional diagnostic but it's practiced by a number of compilers and works safely. But this has nothing to do with packages. The same problem could come up and be just as much a pitfall in one package extending over several files. That's why looking for redefinition is better than looking for symbol clashes.
But in the end, you are really asking a question analogous to a carpenter saying: "Some of these tools, like the ones that cut things, seem to have sharp edges. How am I to know when I cut a piece of wood that I'm not cutting the wrong piece of wood and making a mess of things?" I don't know how carpenters solve this in general, but I bet "good compiler diagnostics" are not the answer. Sometimes you just have to do good bookkeeping in your head and not make a mistake, I think. The only alternative would be to limit the set of tasks to those you could mechanically recognize and warn about, but then Lisp would no longer be useful for that of problems that its linguistic power was up to but its bookkeeping was not up to.
"David McClain" <bar...@qwest.net> writes: > ... Actually, I think that points to the danger inherent in just blindly > USE'ing a package instead of making explicit imports. But then the base LISP > package is a real pain to explicitly import all the things so commonly used. > I think there is a protection mechanism to prevent its symbols from being > redefined.
> But this is a lesson to me -- beware of just blindly USE'ing other packages > without fully realizing what they export.
Yes. I think for a novice, overly confining rules like this are good. Like telling your child that he must always stay on the block of houses he lives on and never cross the street. This kind of rule does not work well in adult life, but adds some security while learning other things and while waiting to be able to master the skills needed to safely cross streets.
Certainly style rules evolve that are short and punchy like this and are worth following until you have deeper knowledge. But see my longer post in response to your original query if you want a more detailed analysis of this problem and why it's good for people to ultimately want to outgrow this initially-productive style rule.
[Since David McClain is a customer of ours, I would normally have sent him email directly telling him of features in Allegro CL, instead of posting here. But since he uses other lisps also, and since the question both he and Kent ask are general and the feature in Allegro CL probably not widely known, I thought I'd respond anyway]
> "David McClain" <bar...@qwest.net> writes: > > [ ... ] > > But I found that by simply using a package inside another client package, I > > could accidentally redefine an imported symbol for my own needs in the > > client package -- and this redefinition actually redefines the used symbol > > back in the original package -- without any warning!! > [ ... ] > Second, I have long wished that compilers would issue a style diagnostic > if you tried to define a variable or function from within a package that > did not belong to that package. That would, of course, get you a lot of > style warnings if you did:
> (in-package "FOO")
> (defun bar:x (x) ...)
> but that's the point. Probably a mechanism could be created for muffling > the warning if vendors experimented with this and negotiated with users about > how to make it tolerable.
We have done just this over the years. It has been a hard problem and several factors have been high in the minds of the implementors that did the work:
- The default behavior must conform to strict ANSI CL. - There must not be too many warnings - When the features are used, there must not be too few warnings. - There must be reasonable circumvention of these locks, both on individual defintions and on whole packages and situations.
Also, instead of warnings, which might be ignored during long compilations when the packages are "locked", we make the conditions errors. But a non-continuable error is too harsh, so we allow a user to control the thwarting of the locking on an individual basis. There is also a way to circumvent the locking mechanism programmatically, in case there is no way to intervene manually.
There are a number of features that we implemented which are described here:
The two I concentrate on here are the package-definition-lock, which causes continuable errors when a definition is made while not "in" the package homing the symbol being defined, and the implementation-package, which describes those packages for which the home package will not complain.
The example below is partly taken from the documentation described in the above section, and partly from David's example. Three packages are defined, foo, bar, and bas. The foo package exports doit, which bas then imports. Also, foo has as implementation-packages itself and bar, which means that symbols in foo will not be locked from definition while in package bar (or, of course, foo). Note in the example run below, package-definition-locks are set for all three packages, so that other than the exceptions, each will complain about definitions on symbols homed within them while in other packages. Note also that I opted for the default restart in each case, which forces the definition anyway.
Note also in the example that the error message includes the string "is a violation for portable programs". This concept was originated in CL by making it illegal to define any functions on symbols in the common-lisp package (the concept is that if more than one user defines the symbol in different ways, the final definition is not predictable, and one of the programs will break). The same concept applies to any package that might be defined by a user, and it is why we provide the same mechanism for user-defined packages as we do for common-lisp and other packages that we define.
CL-USER(1): (defpackage :foo #+allegro (:implementation-packages "FOO" "BAR") (:export #:doit)) #<The FOO package> CL-USER(2): (defpackage :bar) #<The BAR package> CL-USER(3): (defpackage :bas (:use "FOO" "COMMON-LISP") (:import-from "FOO" foo::doit)) #<The BAS package> CL-USER(4): (setf (package-definition-lock (find-package :foo)) t) T CL-USER(5): (setf (package-definition-lock (find-package :bar)) t) T CL-USER(6): (setf (package-definition-lock (find-package :bas)) t) T CL-USER(7): (in-package :bar) #<The BAR package> BAR(8): (defun foo::mysym (a b) (+ a b)) FOO::MYSYM BAR(9): (in-package :foo) #<The FOO package> FOO(10): (defun bar::my-other-sym (c) (sqrt c)) Error: Attempt to make a FUNCTION definition for the name BAR::MY-OTHER-SYM. This name is in the BAR package and defining it is a violation for portable programs. The package BAR has EXCL:PACKAGE-DEFINITION-LOCK set, which causes the system to signal this violation. [condition type: PACKAGE-LOCKED-ERROR]
Restart actions (select using :continue): 0: Set the FUNCTION definition of the name BAR::MY-OTHER-SYM anyway. 1: Return to Top Level (an "abort" restart). 2: Abort entirely from this process. [1c] FOO(11): :cont BAR::MY-OTHER-SYM FOO(12): (defun doit () 15) DOIT FOO(13): (in-package :bas) #<The BAS package> BAS(14): (describe 'doit) DOIT is a SYMBOL. It is unbound. It is EXTERNAL in the FOO package and accessible in the BAS package. Its function binding is #<Interpreted Function DOIT> The function takes arguments () BAS(15): (defun doit () 16) Error: Attempt to make a FUNCTION definition for the name DOIT. This name is in the FOO package and redefining it is a violation for portable programs. Replacing the current definition of #<Interpreted Function DOIT> may be dangerous. The package FOO has EXCL:PACKAGE-DEFINITION-LOCK set, which causes the system to signal this violation. [condition type: PACKAGE-LOCKED-ERROR]
Restart actions (select using :continue): 0: Set the FUNCTION definition of the name DOIT anyway. 1: Return to Top Level (an "abort" restart). 2: Abort entirely from this process. [1c] BAS(16): :cont DOIT BAS(17): (doit) 16 BAS(18):
-- Duane Rettig Franz Inc. http://www.franz.com/ (www) 1995 University Ave Suite 275 Berkeley, CA 94704 Phone: (510) 548-3600; FAX: (510) 548-8253 du...@Franz.COM (internet)
> As defined now, you have no right to a warning. What you're saying is the > same as saying you have write access to someone's directory, you've read
in
I went back to CLHS in detail last night after discovering this situation, and indeed, it conjured up concepts from long ago -- and it does sound very much like having write access to someone else's directory. I now get a picture that indicates one grand namespace containing symbols - only one symbol ever exists by any given name.
But that can't be correct either... how about the keyword package. Somehow, I frequently manage to construct symbols with meanings that would otherwise clash with some keyword symbols. So perhaps the keyword package is treated specially and it isn't a package on the same footing as other packages?
I have long said that to understand a language you have to implement a compiler for it. I haven't done so for CL, and so my ignorance is showing through... Had I implemented a CL, then I might have a deeper understanding of the subtle issues involved. But that is asking an awful lot of the users... to have them implement a language before they start using it.
> > It would seem that barrel-loads of problems could occur downstream, as other > > unsuspecting user packages get their expected behaviors modified by some > > other unknowing client package.
> Do not share symbols with people you don't want this to happen for. > Nothing wrong with doing:
> (defun foo (x) (other:foo x))
> rather than importing the OTHER package.
That would be fine -- if it were known in advance that foo were owned by another package. But when I write code I have no control on the behavior of future programmers. Now that I realize the situation, I can take the time to check ownership of symbols -- but how many of you actually do that when you program?
I would like my application to continue to operate correctly, despite whatever actions an unrelated sibling package might be taking in the future. I see now that CL wasn't written with this in mind. So either I export symbols I deem worthy of use by other packages, or I keep them all local or imported to my package. I can't control the future and whether or not some untrustworthy package decides to utilize what I have provided.
But this very situation makes me wonder about several issues:
1. Suppose a package becomes popular to use in other client packages, then one day, it being loaded into the system ahead of these clients decides to add another exported symbol of its own new making. But now that could break all of those clients because they might also have defined that symbol for themselves. They might break because that new symbol is now used inside the earlier definitions, and when they redefine it, would cause the old exported symbols to malfunction.
I see now that they would have had to come to agreement on the definition of that symbol between themselves, but the old provider package knew nothing of that when it was originally written. And there is no way for the package maintainer to know that this package is popular on the other side of the world...
2. Sibling applications - that run concurrently and know nothing of each other, could inadvertently step on each other by defining actions named the same as chosen in the other package.
------------------- All the more surprising to me... is that for 15 years or so, I have been successfully using CL without having run into this particular situation... How is it that I was able to dance in a mine field without ever having tripped one of them? There is something about CL, in its present form, that seems to make this possibility a rarity. What is that "thing" about CL?
Spoken language is so fraught with ambiguity. I see now that "shadowing" is a term that means something subtly different than what I originally thought. We bring all of our world view with us when we interpret what is said. You can say shadowing - and it conjures up in my mind something a little different that what you really meant. Had I been part of the early history of CL, I would have understood what you wanted me to understand. But I had a different history and I brought that to the table when I read the new rules.
Here is one area where formalism, through language definition equations, could help convey the exact meaning intended. But I have never seen such formalism applied to CL. I see plenty of carefully worded legal documents describing the situation -- more like the fine print in an insurance policy. But that still leaves ambiguity of the spoken language.
I see nothing wrong with the freedoms inherent in CL. But having much freedom means that I need to be aware of the pitfalls of certain actions. When you want to get a license to fly, you have to put in many hours of training before you are permitted to go it alone. You don't just buy the keys to an airplane and start flying. Those hours spent are to help you understand the pitfalls of various actions in the freedom of 3-D travel here on Earth.
I am not saying we need similar training programs in CL (maybe we do?) but I am saying that we need to help future users understand the consequences of their actions. A set of equations describing the actions of the language would help to convey this information, without subtly assuming a shared history and mindset regarding the language.
I am not arguing for changes in the language, nor am I shooting it down. I think adding warnings, as you indicated in your response, could well overwhelm a user with useless information most of the time. Then such warnings would gradually become ignored and fail to have the intended impact when they were really necessary. But how is a compiler to know when a warning is or is not important...
I have a strong interest in making human to computer communication more robust and easier for us as humans. There is a huge impedance mismatch between us non-perfect mortals, and the rigid perfectionism required by computers. I don't have any definite answers to the problem yet, and I am still collecting information on what works and what doesn't work.
>> As defined now, you have no right to a warning. What you're saying is the >> same as saying you have write access to someone's directory, you've read >in
>I went back to CLHS in detail last night after discovering this situation, >and indeed, it conjured up concepts from long ago -- and it does sound very >much like having write access to someone else's directory. I now get a >picture that indicates one grand namespace containing symbols - only one >symbol ever exists by any given name.
>But that can't be correct either... how about the keyword package. Somehow, >I frequently manage to construct symbols with meanings that would otherwise >clash with some keyword symbols. So perhaps the keyword package is treated >specially and it isn't a package on the same footing as other packages?
Programmers don't usually assign function bindings to names in the keyword package, and the system automatically assigns variable bindings to them (they're automatically bound to themselves).
So what kind of conflict are you anticipating that you're not getting? Keywords are usually used just for their identity.
-- Barry Margolin, bar...@genuity.net Genuity, Woburn, MA *** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups. Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
> Programmers don't usually assign function bindings to names in the keyword > package, and the system automatically assigns variable bindings to them > (they're automatically bound to themselves).
Yes, I understood that aspect of CL. And I appreciate that assigning a value and assigning a function value are really two separate issues. The question is, why don't I see function bindings on these keyword symbols if I happen to use a like-named symbol in one of my packages?
Since redefining a function binding on some imported symbol clobbers the prior function binding, why doesn't this happen with keyword symbols too? It must be that the keyword symbols are treated specially, and they must exist in duplicate with respect to like named symbols from other packages. Other packages, however, share like-named symbol instances.
Now, after a few minutes experimenting, I see that I had some confusion, even now, about the management of CL symbols. Non-exported symbols are safe against redifintion in other packages.
That would imply that using syntax p::s is actually a preferable way to use foreign package symbols, and let the foreign package actually keep them as non-exported.
Up to now, I had thought that accessing package internal symbols of another package was a sure way to violate portability. If the package hadn't exported a symbol, then it might have had good reasons for wanting to keep things private.
It is just the exported symbols that are at risk of redefinition. There appears to be no subdivision in the package system between those non-exported symbols intended to be kept private, and those non-exported symbols intended for client access.
So I guess my main misunderstandings had to do with the behavior of the exported package interface. It is not exactly the same thing as an advertised interface definition in other languages -- listing features that are accessible to the user, but which are immutable against user redefinition.
You know, the older I get, the more often I rediscover things which I already knew at an earlier time....
In article <NU9P7.157$Ui5.276...@news.uswest.net>,
David McClain <bar...@qwest.net> wrote: >Yes, I understood that aspect of CL. And I appreciate that assigning a value >and assigning a function value are really two separate issues. The question >is, why don't I see function bindings on these keyword symbols if I happen >to use a like-named symbol in one of my packages?
Because each package is an independent namespace. The symbols P1::FOO, P2::FOO, and :FOO are all different symbols (unless one of the packages uses the other, or symbols are imported), and changes made to one have no effect on the others. As long as you don't (use-package "KEYWORD") you should never have a problem if one of your symbols happens to have the same name as a keyword.
-- Barry Margolin, bar...@genuity.net Genuity, Woburn, MA *** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups. Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
In article <sAaP7.165$Ui5.302...@news.uswest.net>,
David McClain <bar...@qwest.net> wrote: >Now, after a few minutes experimenting, I see that I had some confusion, >even now, about the management of CL symbols. Non-exported symbols are safe >against redifintion in other packages.
>That would imply that using syntax p::s is actually a preferable way to use >foreign package symbols, and let the foreign package actually keep them as >non-exported.
If a symbol isn't exported, you're typically not expected to use it at all from outside the package. They're usually not documented, and subject to removal in future releases.
>Up to now, I had thought that accessing package internal symbols of another >package was a sure way to violate portability. If the package hadn't >exported a symbol, then it might have had good reasons for wanting to keep >things private.
This is usually a good assumption.
>It is just the exported symbols that are at risk of redefinition. There >appears to be no subdivision in the package system between those >non-exported symbols intended to be kept private, and those non-exported >symbols intended for client access.
Being internal or external has little to do with their risk of redefinition. You could do:
(defun p::s ...)
and redefine an internal symbol.
The main reason why you're running into redefinition issues is because you insist on using USE-PACKAGE. Instead, you should access symbols from the other package using P:S syntax.
-- Barry Margolin, bar...@genuity.net Genuity, Woburn, MA *** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups. Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
Yes, I am beginning to grasp what is going on... It does in fact appear that each package is a separate namespace - for non-exported symbols only. Once a symbol is exported it seems to become part of a larger system-wide namespace that is susceptible to casual redefinition -- unless protected by a scheme like that described by Duane.
> Being internal or external has little to do with their risk of > redefinition. You could do:
> (defun p::s ...)
> and redefine an internal symbol.
I had never seen anyone do such a thing... But its syntax cries out that something peculiar is afoot.
> The main reason why you're running into redefinition issues is because you > insist on using USE-PACKAGE. Instead, you should access symbols from the > other package using P:S syntax.
Yes, indeed... Very good point. But that still leaves known working systems susceptible to ignorant programmers like me in the future... Ah well...
"David McClain" <bar...@qwest.net> writes: > "Barry Margolin" <bar...@genuity.net> wrote
> > Programmers don't usually assign function bindings to names in the keyword > > package, and the system automatically assigns variable bindings to them > > (they're automatically bound to themselves).
> Yes, I understood that aspect of CL. And I appreciate that assigning a value > and assigning a function value are really two separate issues. The question > is, why don't I see function bindings on these keyword symbols if I happen > to use a like-named symbol in one of my packages?
> Since redefining a function binding on some imported symbol clobbers the > prior function binding, why doesn't this happen with keyword symbols too? It > must be that the keyword symbols are treated specially, and they must exist > in duplicate with respect to like named symbols from other packages. Other > packages, however, share like-named symbol instances.
A symbol has a name, but that's just important for humans. It is a first-class object, which may or may not live in a package. Symbols in different packages, with the same name, are different objects. Eg:
* (defpackage "CLL-EXAMPLE" (:use) (:export "FOO")) ;[*] #<The CLL-EXAMPLE package, 1/8 internal, 1/2 external> * (in-package "CLL-EXAMPLE") #<The CLL-EXAMPLE package, 1/8 internal, 1/2 external> * 'foo FOO * (cl:describe 'foo) FOO is an internal symbol in the CLL-EXAMPLE package. * (cl:in-package "USER") #<The COMMON-LISP-USER package, 12/21 internal, 0/9 external> * 'foo FOO * (describe 'foo) FOO is an internal symbol in the COMMON-LISP-USER package. * (eq 'user::foo 'cll-example:foo) NIL * (defpackage "CLL-EXAMPLE2" (:use "CLL-EXAMPLE")) #<The CLL-EXAMPLE2 package, 0/9 internal, 0/2 external> * (in-package "CLL-EXAMPLE2") #<The CLL-EXAMPLE2 package, 0/9 internal, 0/2 external> * 'foo FOO * (cl:describe 'foo) FOO is an external symbol in the CLL-EXAMPLE package. * (cl:eq 'foo 'user::foo) COMMON-LISP:NIL * (cl:in-package "USER") #<The COMMON-LISP-USER package, 2/9 internal, 0/9 external> * (defpackage "CLL-EXAMPLE3" (:use) (:export "FOO")) #<The CLL-EXAMPLE3 package, 0/8 internal, 1/2 external> * (eq 'cll-example:foo 'cll-example3:foo) NIL * (defpackage "CLL-EXAMPLE4" (:use "CLL-EXAMPLE" "CLL-EXAMPLE3"))
Error in function USE-PACKAGE: Use'ing package CLL-EXAMPLE results in name conflicts for these symbols: (CLL-EXAMPLE3:FOO)
Restarts: 0: [CONTINUE] Unintern the conflicting symbols in the CLL-EXAMPLE4 package. 1: [ABORT ] Return to Top-Level.
[*] I was going to call this package FOO, but then I noticed that I'd been developing in the FOO package. Oops. So, I'm glad I typed up this example :-)
-- /|_ .-----------------------. ,' .\ / | No to Imperialist war | ,--' _,' | Wage class war! | / / `-----------------------' ( -. | | ) | (`-. '--.) `. )----'
"David McClain" <bar...@qwest.net> writes: > Yes, I am beginning to grasp what is going on... It does in fact appear that > each package is a separate namespace - for non-exported symbols only. Once a > symbol is exported it seems to become part of a larger system-wide namespace > that is susceptible to casual redefinition -- unless protected by a scheme > like that described by Duane.
Yes. You have to be careful.
Note that everything is subject to "casual redefinition"
In file 1
(defun foo (x) x)
In file 2
(defun foo (x) (* 2 x))
Or even in the same file. There is no package issue here. This is just the nature of the beast.
Cheers
-- Marco Antoniotti ======================================================== NYU Courant Bioinformatics Group tel. +1 - 212 - 998 3488 719 Broadway 12th Floor fax +1 - 212 - 995 4122 New York, NY 10003, USA http://bioinformatics.cat.nyu.edu "Hello New York! We'll do what we can!" Bill Murray in `Ghostbusters'.
"David McClain" <bar...@qwest.net> writes: > > The main reason why you're running into redefinition issues is because you > > insist on using USE-PACKAGE. Instead, you should access symbols from the > > other package using P:S syntax.
> Yes, indeed... Very good point. But that still leaves known working systems > susceptible to ignorant programmers like me in the future... Ah well...
But it would also prevent me from doing something like this:
(defmacro dynamic-flet (forms &body body) "Like FLET, but only affects the global function binding, and does so with dynamic, not lexical scope. This useful for porting elisp code." (let ((old-vals (loop for (name) in forms collecting (cons name (gensym)))) (new-vals (loop for (name) in forms collecting (cons name (gensym))))) `(let (,@(loop for (name . sym) in old-vals collecting `(,sym (symbol-function ',name))) ,@(loop for (name . fn) in forms for sym = (cdr (assoc name new-vals)) collecting `(,sym (lambda ,@fn)))) (unwind-protect (progn ,@(loop for (name . sym) in new-vals collecting `(setf (symbol-function ',name) ,sym)) ,@body) ,@(loop for (name . sym) in old-vals collecting `(setf (symbol-function ',name) ,sym))))))
And personally, I'd rather insist that people are comfortable with the CL package system before they hack on real applications, than make it safer for the naive user, but at the cost of power. Besides, once you get the hang of it, it's not confusing. It's just surprisingly object-oriented at first blush.
-- /|_ .-----------------------. ,' .\ / | No to Imperialist war | ,--' _,' | Wage class war! | / / `-----------------------' ( -. | | ) | (`-. '--.) `. )----'
"David McClain" <bar...@qwest.net> writes: > Yes, I am beginning to grasp what is going on... It does in fact appear that > each package is a separate namespace - for non-exported symbols only. Once a > symbol is exported it seems to become part of a larger system-wide namespace
Well, sort of. As soon as a symbol is exported, it becomes available for other packages to import, either by importing it explicitly, or implicitly by use-package. Note however that CL's philosophy is one of openness; it does not disallow reference of any symbol in its system regardless of the package (unlike other languages which actually hide some classes of symbols). Thus, foo::bar can _always_ be accessed as foo::bar, whether in the foo package or not. But the double-colon is an immediate visual red flag that something strange is happening, and to be careful.
> that is susceptible to casual redefinition -- unless protected by a scheme > like that described by Duane.
"casual" is the keyword here. Redefinition of _any_ symbol is possible (unless a locking scheme like I described is in effect), but defining an internal symbol in a different package is intentionally clumsy so that the unsuspecting programmer won't generally run into it accidentally.
-- Duane Rettig Franz Inc. http://www.franz.com/ (www) 1995 University Ave Suite 275 Berkeley, CA 94704 Phone: (510) 548-3600; FAX: (510) 548-8253 du...@Franz.COM (internet)
>>>>> "Kent" == Kent M Pitman <pit...@world.std.com> writes:
Kent> [...] But CLHS is a specification, not a tutorial. [...]
I have often wished I knew where to find a tutorial on packages and also pathnames. I don't see the subject treated very thoroughly in the CL literature. If I am merely blind, I would like to be healed, if possible.
-- Russell Senior ``The two chiefs turned to each other. seni...@aracnet.com Bellison uncorked a flood of horrible profanity, which, translated meant, `This is extremely unusual.' ''
"David McClain" <bar...@qwest.net> writes: > Yes, I am beginning to grasp what is going on... It does in fact appear that > each package is a separate namespace - for non-exported symbols only. Once a > symbol is exported it seems to become part of a larger system-wide namespace > that is susceptible to casual redefinition -- unless protected by a scheme > like that described by Duane.
You're confused in your explanation.
Packages are not subject to redefinition or not.
Even from another package, you can do
(defun foo::bar (x) ...)
and redefine an internal symbol of another package with ill results. The issue of redefinition is not package-related. Packages are just mechanisms that help you gain access to objects. The objects, once accessed, are all treated uniformly. No statement talks about packages being susceptible or non-susceptible to redefinition is going to be right. Symbols are what get defined or redefined. Packages control the naming conventions for getting to symbols, but no package denies you access to any other if you're willing to do the right notation. And no redefinition is ever safe unless you've studied the code and decided you want the redefinition in the case of all uses, independent of what notation was used to gain access in each of those uses. Packages offer you the ability to control the relative ease with which you have access to symbols, but that's pretty much all.
Russell Senior <seni...@aracnet.com> writes: > >>>>> "Kent" == Kent M Pitman <pit...@world.std.com> writes:
> Kent> [...] But CLHS is a specification, not a tutorial. [...]
> I have often wished I knew where to find a tutorial on packages and > also pathnames. I don't see the subject treated very thoroughly in > the CL literature. If I am merely blind, I would like to be healed, > if possible.
Yeah, they are mostly obsessed with producing armies of programmers capable of writing reverse, just in case there's still a language that doesn't have that capability. I made a note for my books-in-progress. I agree this is a good set of topic areas. Thanks! Sorry I don't have such info already handy to offer tho... --Kent
* Kent M Pitman | Yeah, they are mostly obsessed with producing armies of programmers | capable of writing reverse, just in case there's still a language that | doesn't have that capability.
Heh. The often stupid exercises we find in introductions to (Common) Lisp may in fact be a _good_ reason for someone who is introduced to the langauge this way to avoid (Common) Lisp in the future. What _do_ they tell people but that (Common) Lisp lacks all useful features? Not only does the language impose a whole new way of thinking about things, it has a whole new way of doing all the stuff they learned in Algorithms 101.
Real (Common) Lisp programmers love the language for the fact that they do _not_ have to implement everything from scratch. (If they wanted that, they would have chosen Scheme, and, indeed, most of the idiotic exercises in introductions to "Lisp" are really for Scheme.) I think this simple fact completely eludes those who write introductory books, because it is somehow not considered pedagogically kosher to assume that programmers are smart enough to pick up most of the simple things in the first lecture (nor to ignore those who do not).
I do not think it is the parentheses or the prefix syntax that "drive people away" at all, but rather the annoying condescension towards new programmers in the books they are offered to learn the language from, as if they needed to learn how to implement basic algorithms, as if the prefix syntax and the parentheses somehow need to be over-focused on in an apologetic way.
The trivial functions they are asked to implement is the kind of stuff they should be exposed to so they could _read_ good source code instead of having to write little toy versions that are broken. Also, if they read good code for these non-trivial things, they would understand just how good they are in practical use, which their own versions of them would frankly not be. Moreover, the real code to implement reverse and other useful functions may be vastly more complex than the usual simple stuff that people are confronted with, or implement, with real concerns about not wasting resources and being very efficient.
After all, the reason we have a big language is so people can be relieved of doing everything with stone-age tools. To trust that everything is implemented well, however, one might need to provide programmers with actual source code and a demonstration how to make these things work correctly and efficiently. Franz Inc does this to paying customers, but I do not know how other commercial vendors do this.
/// -- The past is not more important than the future, despite what your culture has taught you. Your future observations, conclusions, and beliefs are more important to you than those in your past ever will be. The world is changing so fast the balance between the past and the future has shifted.
"David McClain" <bar...@qwest.net> wrote in message <news:gOaP7.173$Ui5.309970@news.uswest.net>... > Yes, indeed... Very good point. But that still leaves known working systems > susceptible to ignorant programmers like me in the future... Ah well...
I think this is partly an issue of documentation and idiocy (I'm *not* implying you are an idiot. You or I may do idiotic things (I do, all the time) but that's a different statement. For the record I don't think you are.)
If I provide some system to you, then I'd expect to also provide documentation for it which would say stuff like `the class FOO does xxx' and `the function BAR does yyy' and `the following functions are not yet implemented: GRUMBLE, WHIRR'. If the names in the interface to my system are exported from the TFBS-WHIRR-SYSTEM package, then I'd expect that *just* the symbols I've documented are exported and, further, that you've read the documentation and what it says about whether you can redefine/add methods to/whatever things, and don't randomly go around clobbering bits of my system. Similarly I'd expect that, if I secretly export some symbol I've failed to document, then if you clobber this, well that's my problem.
Now, of course, 98% of everything is not documented, so this doesn't really apply, but I think in the good case it should be reasonably clear that after you've a (use-package :tfbs-whirr-system) then you've undertaken not to clobber the symbols I've told you not to (and I've undertaken not to export stuff I haven't told you about).
>>>>> "Kent" == Kent M Pitman <pit...@world.std.com> writes:
Kent> Russell Senior <seni...@aracnet.com> writes: >> >>>>> "Kent" == Kent M Pitman <pit...@world.std.com> writes: >> Kent> [...] But CLHS is a specification, not a tutorial. [...] >> >> I have often wished I knew where to find a tutorial on packages and >> also pathnames. I don't see the subject treated very thoroughly in >> the CL literature. If I am merely blind, I would like to be healed, >> if possible.
Kent> Yeah, they are mostly obsessed with producing armies of programmers Kent> capable of writing reverse, just in case there's still a language that Kent> doesn't have that capability. I made a note for my books-in-progress. Kent> I agree this is a good set of topic areas. Thanks! Sorry I don't have Kent> such info already handy to offer tho...
Add conditions and "proper" use of conditions to that list. :-)
I always have problems with this because I don't do it often enough to remember.
* Raymond Toy <t...@rtp.ericsson.se> | * Kent M Pitman <pit...@world.std.com> | | * Russell Senior <seni...@aracnet.com> | | | | | | I have often wished I knew where to find a tutorial on packages and | | | also pathnames. I don't see the subject treated very thoroughly in | | | the CL literature. If I am merely blind, I would like to be healed, | | | if possible. | | | | Yeah, they are mostly obsessed with producing armies of programmers | | capable of writing reverse, just in case there's still a language that | | doesn't have that capability. I made a note for my books-in-progress. | | I agree this is a good set of topic areas. Thanks! Sorry I don't have | | such info already handy to offer tho... | | Add conditions and "proper" use of conditions to that list. :-) | | I always have problems with this because I don't do it often enough to | remember.
I learned a lot about conditions from the following paper:
Kent M. Pitman, "Condition handling in the Lisp language family," in Advances in Exception Handling Techniques, Alexander Romanovsky, Christophe Dony, Jørgen Lindskov Knudsen, and Anand Tripathi, Eds., 2001, vol. 2022 of Lecture Notes in Computer Science, pp. 39--59, Springer.