From: Johann Hibschman
Subject: Namespaces, packages, and methods
Date: 
Message-ID: <130520011715543536%johannh@uclink.berkeley.edu>
Hi folks,

I've been trying to learn CLOS, and I've become confused by some
naming issues.

In a few classes, I've found that I wanted to define a method with
the same name as a CL built-in function, such as "find", "position",
and "reverse".

I cannot simply define such a method, since it conflicts with the
normal definition of the function.  I can't define it in a package,
since I would then have to not include the common lisp package and
preface all of my CL symbols with "cl::".

Even if I could define such a thing within a package, I could not
include two packages, each of which defines a method, into the same
program without conflicts.

Is there anything I can do, or is this just something I have to learn
not to do?  This just grates on me somehow, since I come from languages
where a class is its own namespace and such issues never arise, but
perhaps I'm misunderstanding things.

What happens if I have foo::method and bar::method and then try to 
load both foo and bar into one file?

I'm afraid I'm rambling a bit, but the interaction between the idea of
generic functions, which add specializations to a global object, and
packages confuses me.  If you could lend any insight, I'd appreciate
it.

Thanks,

--Johann

From: Kent M Pitman
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <sfwy9s0lqf4.fsf@world.std.com>
Johann Hibschman <·······@uclink.berkeley.edu> writes:

> I've been trying to learn CLOS, and I've become confused by some
> naming issues.
> 
> In a few classes, I've found that I wanted to define a method with
> the same name as a CL built-in function, such as "find", "position",
> and "reverse".
> 
> I cannot simply define such a method, since it conflicts with the
> normal definition of the function.  I can't define it in a package,
> since I would then have to not include the common lisp package and
> preface all of my CL symbols with "cl::".

No, you can do it.

Check out the ability to shadow and shadowing-import. 

In this case, just

 (defpackage "MY-PACKAGE"
   (:use "CL")
   (:shadow "FIND" "POSITION" "REVERSE"))

will leave you ok.

> Even if I could define such a thing within a package, I could not
> include two packages, each of which defines a method, into the same
> program without conflicts.

This is what :shadowing-import-from is for.

 (defpackage "MY-PACKAGE-2"
   (:use "CL" "MY-PACKAGE")
   (:shadowing-import-from "MY-PACKAGE"
      "FIND" "POSITION")
   (:shadowing-import-from "CL"
      "REVERSE"))

> Is there anything I can do, or is this just something I have to learn
> not to do?  This just grates on me somehow, since I come from languages
> where a class is its own namespace and such issues never arise, but
> perhaps I'm misunderstanding things.

Most languages have to confront the issue of inheriting names, and, when
such inheritance comes into conflict, resolving the conflict.  CL's tools
for this are not perfect, but they can be made to work.
 
> What happens if I have foo::method and bar::method and then try to 
> load both foo and bar into one file?

I'm not sure I understand your question.  Is method an actual symbol
here or a metasyntactic variable meaning "some arbitrarily selected
method name"?  What does it mean to load foo and bar into a file?
Files are not something you load into--they are something you load
from.  If you clarify your question, it can probably be answered.  I'm
too lazy to answer all the possible questions you might have asked.

> I'm afraid I'm rambling a bit, but the interaction between the idea of
> generic functions, which add specializations to a global object, and
> packages confuses me.  If you could lend any insight, I'd appreciate
> it.

Generic functions add specializations to types, not names.

Types have meaning only in how they relate to other types by type inheritance.

Symbols are a way of naming classes.  Different symbols name different classes.

Symbols also name generic functions.  Different symbols name different gf's.

Objects are not specialized, classes are.

I'm not trying to be obtuse but I'm just not sure what your question is.
Perhaps you could rephrase it or offer a worked example.
From: Johann Hibschman
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <140520011043503603%johannh@uclink.berkeley.edu>
In article <···············@world.std.com>, Kent M Pitman
<······@world.std.com> wrote:

> No, you can do it.
> 
> Check out the ability to shadow and shadowing-import. 

Ah, thank you, that helps.

> > What happens if I have foo::method and bar::method and then try to 
> > load both foo and bar into one file?
> 
> I'm not sure I understand your question.  Is method an actual symbol
> here or a metasyntactic variable meaning "some arbitrarily selected
> method name"?  What does it mean to load foo and bar into a file?
> Files are not something you load into--they are something you load
> from.  If you clarify your question, it can probably be answered.  I'm
> too lazy to answer all the possible questions you might have asked.

That was rather astoundingly unclear, I admit.

What I meant to ask was, if I have two packages each of which defines a
generic function on a symbol interned in that package, such as

---
(defpackage "PEOPLE" ...)
(in-package "PEOPLE")
(defclass person ...)
(defmethod eat ((p person) food) ...)
---
 and
---
(defpackage "ANIMALS" ...)
(in-package "ANIMALS")
(defclass cat ...)
(defmethod eat ((c cat) food) ...)
---

is there a way that I could merge the definitions of these two functions
so that elsewhere I could write

  (eat thing food)

and have it dispatch correctly on either a person or a cat?

As I understand it, the above code definies a people:eat symbol and a
animals:eat symbol, each of which names a separate generic function,
which would make what I ask impossible.  Is this correct?

Also, would I stylistically be better off naming these functions
animal-eat and person-eat, i.e. with a class prefix, so as to avoid
possible name clashes?

Thanks,

--Johann
From: Johann Hibschman
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <150520010934372829%johannh@uclink.berkeley.edu>
In article <···············@world.std.com>, Kent M Pitman
<······@world.std.com> wrote:

First off, thanks for replying, and thanks to everyone else who's
posted.  I'm slowly integrating a picture of how this system works.
The simple language rules aren't enough for me to deduce the most
natural way to things, so examples help.
 
> > Also, would I stylistically be better off naming these functions
> > animal-eat and person-eat, i.e. with a class prefix, so as to avoid
> > possible name clashes?
> 
> No.  That's generally the wrong thing.

That's what I thought.  There seems to be some tension between the
names produced by defstruct and the most "natural" accessor names
for classes.  I'd started by naming everything with the full prefix,
but have been growing more and more dissatisfied with that.

> Over time, I've been gradually fixing all the FOO-NAME and BAR-NAME
> operators in my code to just be NAME, for example...

I first started wondering about some of these issues when I was working
on a package of generic string functions.  I tried to rename
STRING:STRING-FIND to STRING:FIND and, since I had missed the shadowing
abilities of packages, ground to a halt.

Thanks,

--Johann
From: Kent M Pitman
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <sfwpudaqypt.fsf@world.std.com>
Johann Hibschman <·······@uclink.berkeley.edu> writes:

> In article <···············@world.std.com>, Kent M Pitman
> <······@world.std.com> wrote:
> 
> ... There seems to be some tension between the
> names produced by defstruct and the most "natural" accessor names
> for classes.  I'd started by naming everything with the full prefix,
> but have been growing more and more dissatisfied with that.

Yes, this is a historical issue.  DEFSTRUCT goes way farther back in
time than does DEFCLASS, and before there were generics with methods,
defstruct's naming was needed to keep things straight.  Even today,
when there could be a single method spanning multiple, disjoint parts
of the class hierarchy, some efficiency gain comes for structs from
asssuming a hierarchical type system and lack of redefinition, and
this efficiency aspect of structures is one reason they ahven't been
wholly phased out.

One thing to understand about Lisp is that its design has embraced
gradual change over time, usually by not phasing out the old features
just because new ones came along until they really subsume the old.
And it often offers multiple forms of functionality supporting various
expressional styles.  Some criticize the language for the occasional
apparent design inconsistency this exposes, but then others appreciate
it for its stability, not breaking old programs just because new ways
come along.  Years ago, the languaeg used to change about every monday
and it was painful to the extreme; at some point, we realized
stability was necessary to people building product. CL exists for
various reasons, but one was to address that issue.
From: Erik Naggum
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <3198831829789799@naggum.net>
* Johann Hibschman <·······@uclink.berkeley.edu>
> ... since I come from languages where a class is its own namespace and
> such issues never arise ...

  What you are really saying here is that you have been able to internalize
  the rules of that language such that you do not cause the issues to arise.
  Thsoe who have internalized the rules of Common Lisp do not want to call
  their functions "find" or "position" or "reverse".  It is that simple.

  Different language, different issues.  If you are unwilling to learn to
  work _in_ the language you are now using/learning, but somehow think that
  you should apply the rules and internalizations of another language to
  it, you will in fact never become comfortable with _any_ other language
  than your first.  I _suspect_ that the way you learned your first
  language was not a process of understanding, but a process of copying.
  This is quite common, but as people go on to learn more languages, they
  find that memory and patterns of doing things work well for one language
  (or context) at a time.  It is also faster to do things by memorized
  patterns than to think, so the process of learning a new language has
  some serious drawbacks.  Some languages are even so hard to learn that
  they force people to use but a subset of their patterns of doing things.
  Both Perl and C++ come to mind.  Learning by doing is sometimes touted as
  a good way to learn, but I think it is a good way to learn only one thing
  of a kind.  To learn something different, you first have to study what
  you already know to understand why, not just know how.

#:Erik
-- 
  Travel is a meat thing.
From: Johann Hibschman
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <140520011056460153%johannh@uclink.berkeley.edu>
In article <················@naggum.net>, Erik Naggum <····@naggum.net>
wrote:

> * Johann Hibschman <·······@uclink.berkeley.edu>
> > ... since I come from languages where a class is its own namespace and
> > such issues never arise ...
> 
>   What you are really saying here is that you have been able to internalize
>   the rules of that language such that you do not cause the issues to arise.
>   Thsoe who have internalized the rules of Common Lisp do not want to call
>   their functions "find" or "position" or "reverse".  It is that simple.

If I create a class, implementing an array-like collection, perhaps a
ring-like buffer, what would you recommend I name the function to
return the index of a given item?  Would "ring-position" be better
style?

That approach seems to suggest that all methods names be prefixed by the
name of their primary or base class, which seems (at first glance)
awkward, overly verbose, and redundant, since the type information is
already stored in the dispatch table of the generic function.

Thanks,

--Johann
From: Chris Riesbeck
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <riesbeck-B44E5E.12303115052001@news.it.nwu.edu>
In article <···············@world.std.com>, Kent M Pitman 
<······@world.std.com> wrote:

>Choosing a naming convention that is memorable is handy, though.
>It's tough because FIND is a verb while POSITION  is a noun and

Actually, both words are nouns and verbs. FIND as noun
in code seems less likely than POSITION as verb.

>REVERSE is either a verb or an adjective.

or noun, e.g., "it's just the reverse."
From: Joel Ray Holveck
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <y7cd79atg80.fsf@sindri.juniper.net>
> Many consider it an annoyance that FIND and friends are not generic.
> There were some good reasons for not making them be, but there are
> arguments on the other side as well.  It's a lamentable situation
> either way.

Not to incite a war here, but what are some good reasons for the
status quo?

Thanks,
joelh
From: Kent M Pitman
Subject: Re: Namespaces, packages, and methods
Date: 
Message-ID: <sfw66f25htm.fsf@world.std.com>
Joel Ray Holveck <·····@juniper.net> writes:

> > Many consider it an annoyance that FIND and friends are not generic.
> > There were some good reasons for not making them be, but there are
> > arguments on the other side as well.  It's a lamentable situation
> > either way.
> 
> Not to incite a war here, but what are some good reasons for the
> status quo?

I wondered if anyone would pick up on that. :-)

When generic functions first came out, there wasn't a lot of experience
in writing them--not specifying methods for them, but figuring out how
to document them, etc.  How to navigate the fine line between genericity
and overloading.

For example, suppose we make + be generic and then someone DOES do
(defun add (x y) (+ x y)).  This used to be a reliable function that
would either add two numbers or signal an error, at least in safe mode.
People rely implicitly not just on the normal effect, but also on the
idea that it won't invite people to pass wrong arguments.  

e.g., if you add that stupid 
 (defmethod + ((string1 sring) (string2 sring))
   (concatenate 'string string1 string2)) 
thing that everyone seems to adore so much, how does that affect your
symbolic algebra system when someone accidentally goes to integrate a
string instead of a symbol?  Probably the computation proceeds and the
result is goofy.  Contrary to the opinions of programming newbies, not
all error messages are bad to get--sometimes they save you a lot of time
over having the computation proceed and produce wrong results, or worse,
wrong effects.

And you know that stupid programming question that always comes up in
FAQs, about "finding" a symbol "anywhere in a list, even if hidden by
parens"?  I can see someone modifying FIND to look into multiple levels
of a list, probably thinking they are "helping" since the system version
didn't do this.  And if not this, they'd find some other place they thought
the system was deficient and invent multiple incompatible ways for different
packages to "extend" the system functionality so that packages with
different such theories could no longer be loaded at the same time.

As more things become acceptable, programs will tend to proceed executing
past the conceptual point of error farther to some other point that someone
perhaps just didn't have time to make generic.

People have said this of Teco, for example, where every command is a
single letter and where to the unaccustomed, you'd think that meant
every text sequence is a valid program in the language.  (In fact,
violated argument conventions cause things to trap in short order, but
you can still execute for a ways before you run into trouble.)

I call languages that err for very few reasons "dense" and languages that
err in many situations "sparse".  There is some value to a sparse language
because it recognizes errors in a timely way and doesn't just turn good
data to bad at the point of error and then continue merrily on its way.

Also, there are patterns of usage that we needed more experience with
in order to do definitions of things right.  There are sealing issues
that are needed in order to get good efficiency on some opoerators,
and we had not added any sealing primitives.  

And then there are specificational issues like the relationship between
DESCRIBE and DESCRIBE-OBJECT, PRIN1 and PRINT-OBJECT.  The question of
whether the function you call is the one you specialize, etc.  Consider
the following bug that really took me by surprise when I first saw it back
in the days of the design of NIL (the New Implementation of Lisp) circa
1980.  I'll write this in no particular dialect since I don't remember
the primitives available--just assume things mean waht they look like:

 (defgeneric length (thing))

 (defmethod length ((string string))
   (string-length string))

 (defmethod length ((empty null)) 0)

 (defmethod length ((cons cons))
   (+ 1 (length (cdr cons))))

 (length '(a b c))       => 3 ;ok
 (length "abc")          => 3 ;ok
 (length '(a b . "cde")) => 5 ;hmm

I'd prefer this last one signal an error.  But specifying LENGTH in a way
that avoids this outcome is pretty tricky.  You want the definition of the
defgeneric's purpose to be to describe a behavior so clearly that everyone
will be able to adhere, but what would you have written  (before you saw
any of the methods) in such a way as to have avoided this.  

The single effect of this one operator may seem trivial to you, but if you
made a whole language out of massively generic setups, my personal intuition
is that you'd get many such interactions and they would be very, very tricky
to isolate.

Also, people often talk about some functions as if they had no definition
when they do. e.g., EQUAL.  People often talk about how they wish EQUAL
would be defined for classes.  But EQUAL *is* defined on their class--they
just don't like the definition.  They want to give it a *different* meaning.
That's not the same thing.  We might add another functio, ==, as so many
other languages have done that is defined by "compares two objects in a way
not globally predictable or consistent but that makes the implementor of
one or both of the classes be probably-happy".  Maybe there's a better way
to describe the function, but I've never seen anyone go to the trouble.
They just take a fuzzy function and hope that's enough.  Even EQUALP is
fuzzy due to its' exposing which classes are based on structures and which
on standard-class, and that's trouble enough.  A totally generic EQUAL would
lead to all sorts of unreliability of EQUAL and might break all kinds of
programs due to user-errors in specifying methods that previously could not
be broken.
From: Rob Warnock
Subject: sparse & dense [was: Re: Namespaces, packages, and methods ]
Date: 
Message-ID: <9dsud2$8lv9n$1@fido.engr.sgi.com>
Kent M Pitman  <······@world.std.com> wrote:
+---------------
| I call languages that err for very few reasons "dense" and languages that
| err in many situations "sparse".  There is some value to a sparse language
| because it recognizes errors in a timely way and doesn't just turn good
| data to bad at the point of error and then continue merrily on its way.
+---------------

This is precisely why I prefer "sparse" command-line shell command sets --
because I frequently make the mouse-o blunder of accidentally pasting a
selection-buffer full of random text into a shell window. Oops!! Sparse
shell languages are (somewhat) less likely to (say) delete all your files.


-Rob

p.s. Actually, in the above case what I probably really want is a
large average Hamming distance between the shell language and the
things I typically select with the mouse. Since things I select
are typically either programs or plaintext, I guess that means I
want shell commands that look like *neither*! (Hmmm... And people
say that Unix commands are "unfriendly"... Maybe not!)

-----
Rob Warnock, 31-2-510		····@sgi.com
SGI Network Engineering		<URL:http://reality.sgi.com/rpw3/>
1600 Amphitheatre Pkwy.		Phone: 650-933-1673
Mountain View, CA  94043	PP-ASEL-IA
From: Kent M Pitman
Subject: Re: sparse & dense [was: Re: Namespaces, packages, and methods ]
Date: 
Message-ID: <sfwk83hfzjk.fsf@world.std.com>
····@rigden.engr.sgi.com (Rob Warnock) writes:

> Kent M Pitman  <······@world.std.com> wrote:
> +---------------
> | I call languages that err for very few reasons "dense" and languages that
> | err in many situations "sparse".  There is some value to a sparse language
> | because it recognizes errors in a timely way and doesn't just turn good
> | data to bad at the point of error and then continue merrily on its way.
> +---------------
> 
> This is precisely why I prefer "sparse" command-line shell command sets --
> because I frequently make the mouse-o blunder of accidentally pasting a
> selection-buffer full of random text into a shell window. Oops!! Sparse
> shell languages are (somewhat) less likely to (say) delete all your files.

Yes, this is one reason that machine languages usually have "0" be an
illegal opcode, btw.  It's assumed that data areas are full of 0's and
if you accidentally execute them, you will have some reason to stop.

> p.s. Actually, in the above case what I probably really want is a
> large average Hamming distance between the shell language and the
> things I typically select with the mouse. Since things I select
> are typically either programs or plaintext, I guess that means I
> want shell commands that look like *neither*! (Hmmm... And people
> say that Unix commands are "unfriendly"... Maybe not!)

Interesting.