vfr...@netcom.com (Will Hartung) writes:
> Also, given proper declarations, will a CLOS compiler ever do the
> dispatch at compile time rather than a dynamic lookup at runtime? Or
> is this counter to CLOS philisophically.
The answer to this is really yes and no. It hangs on what you mean to
include under the heading of "proper declarations." The problem with
compile time dispatch is that one is always free to add more classes
later, at runtime. This means that one cannot reliably do compile-time
dispatch without adding a mechanism (or declaration) that a particular
class (sub-) hierarchy is frozen and will not be extended.
________________________________________________________________________
Thomas A. Russ, Senior Research Scientist t...@isi.edu
In the "Fast/Slow - Tastes Great/Less Filling" thread(s), there was the issue
of using C++ methods compared to CLOS methods, and the performance hit
of using them.
I would have to suppose that we're talking about C++ methods that are
dispatched at compile time versus the essentially virtual methods of
CLOS's generic functions.
I think it would be clear that a static method invocation would be
faster than a dynamic one.
But I'm wondering how much the CLOS method dispatch varies from the
C++ dispatch for a virtual method. I guess it is a bit of a tough
call, as CLOS is pretty rich on its list of potential methods to run.
Simply, it's not only looking for a specific method, but perhaps a
list of methods, and what order are they be invoked.
Also, I was wondering if the MOP would enable someone to lookup the
actual function for a generic function call so it can be used directly
within a tight loop to save the dispatching costs. I've heard of folks
doing this in Objective-C. I would think that this is a classic
example of why we have a MOP in the first place. But, then, I can see
it being difficult as well.
Also, given proper declarations, will a CLOS compiler ever do the
dispatch at compile time rather than a dynamic lookup at runtime? Or
is this counter to CLOS philisophically.
Thanx!
--
Will Hartung - Rancho Santa Margarita. It's a dry heat. vfr...@netcom.com
1990 VFR750 - VFR=Very Red "Ho, HaHa, Dodge, Parry, Spin, HA! THRUST!"
1993 Explorer - Cage? Hell, it's a prison. -D. Duck
This is generally referred to as the "effective method" or "combined
method".
>Also, I was wondering if the MOP would enable someone to lookup the
>actual function for a generic function call so it can be used directly
>within a tight loop to save the dispatching costs. I've heard of folks
>doing this in Objective-C. I would think that this is a classic
>example of why we have a MOP in the first place. But, then, I can see
>it being difficult as well.
Yes, I think the MOP could be used to do this.
Note that most production CLOS implementations do quite a bit of method
caching, and I wouldn't be surprised if a real high-performance
implementation were to implement the above lookup automatically. It's
similar to the common optimization technique of code motion for loop
invariants. However, CLOS has the problem that the effective method may
not be invariant, since a function called within the loop could add or
delete methods to another generic function within the loop.
The added dynamism of Lisp does make it harder to generate optimal code.
However, tightness of generated code is not the only benchmark for a
language.
--
Barry Margolin, bar...@bbnplanet.com
BBN Corporation, Cambridge, MA
Support the anti-spam movement; see <http://www.cauce.org/>
Please don't send technical questions directly to me, post them to newsgroups.
From: Barry Margolin <bar...@bbnplanet.com>
Newsgroups: comp.lang.lisp
Date: 19 Aug 1997 05:18:17 -0400
Organization: BBN Planet, Cambridge, MA
Lines: 37
Sender: bar...@pasilla.bbnplanet.com
References: <vfr750EF...@netcom.com>
NNTP-Posting-Host: pasilla.bbnplanet.com
...
Yes, I think the MOP could be used to do this.
Note that most production CLOS implementations do quite a bit of method
caching, and I wouldn't be surprised if a real high-performance
implementation were to implement the above lookup automatically. It's
similar to the common optimization technique of code motion for loop
invariants. However, CLOS has the problem that the effective method may
not be invariant, since a function called within the loop could add or
delete methods to another generic function within the loop.
The added dynamism of Lisp does make it harder to generate optimal code.
However, tightness of generated code is not the only benchmark for a
language.
This is all good, but has there been any attempt yet to agree upon a
set of conventions which will facilitate the compiler to produce
tighter code? This has been an issue in the Dylan development
("sealed" classes) and it is seen in Java as well ("final"
declarations). I.e. has anybody produced a proposal which Franz and
Harlequind can digest?
Cheers
--
Marco Antoniotti
==============================================================================
California Path Program - UC Berkeley
Richmond Field Station
tel. +1 - 510 - 231 9472
> Also, I was wondering if the MOP would enable someone to lookup the
> actual function for a generic function call so it can be used directly
> within a tight loop to save the dispatching costs. I've heard of folks
> doing this in Objective-C. I would think that this is a classic
> example of why we have a MOP in the first place. But, then, I can see
> it being difficult as well.
Seems to me that the quick accessors implementation (qacc) from Gregor
Kiczales and Luis Rodriguez described in [1] is a step in that direction
[1] Gregor Kiczales, Luis Rodriguez, Efficient Method Dispatch in PCL,
Xerox
PARC Technical Report, SSL-89-95, [P89-00140], 1989.
Eric Sauthier
> Note that most production CLOS implementations do quite a bit of
> method
> caching, and I wouldn't be surprised if a real high-performance
> implementation were to implement the above lookup automatically.
> It's
> similar to the common optimization technique of code motion for
> loop
> invariants. However, CLOS has the problem that the effective
> method may
> not be invariant, since a function called within the loop could add
> or
> delete methods to another generic function within the loop.
>
> The added dynamism of Lisp does make it harder to generate optimal
> code.
> However, tightness of generated code is not the only benchmark for
> a
> language.
>
> This is all good, but has there been any attempt yet to agree upon a
> set of conventions which will facilitate the compiler to produce
> tighter code? This has been an issue in the Dylan development
> ("sealed" classes) and it is seen in Java as well ("final"
> declarations). I.e. has anybody produced a proposal which Franz and
> Harlequind can digest?
It is unlikely that even a "sealed" generic function could be as
efficient as virtual functions in C++. I see at least two problems:
1. Virtual functions in C++ are all declared within the scope of a set
of classes known to the compiler. This means they can all be numbered
and put in a table. When the compiler sees a call to a virtual
function, it figures out its number in the table and converts the call
to an indirect call through an access to the table (which itself can be
accessed through a given slot in the first argument, the 'this'
object). It would be a severe limitation on the Lisp style of
compilation to require the compiler to number all generic functions in
this way, not least because the discriminating argument - assuming
there's only one - is not necessarily in a single class hierarchy. In
other words, given a generic function call (foo x), the compiler would
need to know the root class of 'x' and all of the generic functions
whose first argument discriminates on that root class. The style of
modularization used in Lisp programming makes this a dicey bargain.
2. Multimethods would effectively defeat any attempt to rival C++
virtual function performance.
Basically, you can get the same performance as C++ virtual functions in
Lisp if you give the same information to the compiler that you do in
C++. However, one of the great things about Lisp as a programmer
productivity tool is that you don't have to give all that information -
you can add new methods to generic functions in any module, for any
class, as you wish, without declaring variable types. So IMHO it's
unlikely we'll ever see the set of conventions you're asking for.
I did once implement a virtual function mechanism for Ilog Talk just to
see if I could do something as fast as C++, and it worked fine. But it
was an academic exercise since the restrictions were as severe as C++
and thus not appealing to very many Lisp programmers. (To do this I
made a new metaclass whose instances stored a virtual function table,
then required all the virtual functions to be declared when an instance
of that class was created. Then each virtual function stored its number
and, by definition, was assigned to a single class hierarchy. When
called, the virtual function would look up the vfun table in its first
argument, get the method by indexing its number, and call the method.
An easy hack, but I just wanted to prove that there was nothing inherent
in the parentheses that made the language inefficient - it's all a
question of compile-time knowledge.)
-- Harley
It is possible to have both fast dispatch in CLOS
and support its other advanced features. I have built such an
implementation. It is outlined in my "Under the Hood" JOOP column,
which month/year I can't remember (ask the Franz sales people in...@franz.com
for a reprint).
The implementation must be somewhat slower than C++, because it has
to check for the non-fast case, and backoff to the more general
full CLOS supported cases, which C++ doesn't have to do, but that
is done by a single type check and branch.
Basically a generic function method dispatch looks like this
at the Lisp code level. My true implementation compiles into
code that uses inline machine instructions that have the indexes
linked/patched into the machine code at load-time without bounds checking,
the (standard-instance-tables a) translates into a single dereference,
and furthermore the "funcall" compiles into a low-level tail-call jump rather
than a real funcall invocation.
(defun generic-function-with-three-required-args (a b c)
(if (not (standard-instance-p a))
(slow-generic-dispatch <generic-function-name> a b c)
(funcall (svref (aref (standard-instance-tables a) <package-index>)
<generic-function-index> )
a b c)
)
Each package has an index and each generic-function has an index,
and each instantiated class (one with instances) has a method table.
The function in the table can itself be a discriminator function
that further descriminates on the second or third argument if needed,
or may also invoke before/around/after methods. Whatever it must
do, it stuffs the code to do it in the table. If it's a simple method,
the code will run directly out of the table.
It is totally optimized for method discrimination on the type of
the first CLOS instance argument, and as such can
be competitive to the speed of C++ virtual functions.
-Kelly Murray k...@franz.com http://www.franz.com
From: Harley Davis <da...@ilog.com>
Newsgroups: comp.lang.lisp
Date: Wed, 20 Aug 1997 17:51:59 -0700
Organization: Ilog
Lines: 75
References: <vfr750EF...@netcom.com> <5tbocp$7...@pasilla.bbnplanet.com> <scf67t2...@infiniti.PATH.Berkeley.EDU>
...
It is unlikely that even a "sealed" generic function could be as
efficient as virtual functions in C++. I see at least two problems:
...
2. Multimethods would effectively defeat any attempt to rival C++
virtual function performance.
This is all fine, but my point was not to "make generic functions as
fast af C++ virtual". My question was whether there has been some
effort to add some declarations to CLOS in a standard way in order to
convey more information to the compiler about the nature of the
function call (or declaration) at hand.
But it would be nice to have a set of conventions that at least
*allowed* a compiler to generate very fast code where that was
possible. I hate it that I simply can't *say* to the system `I
promise never to redefine this class or any of its superclasses' or `I
promise never to add any methods to this GF'. Quite apart from any
other issues these are valuable documentation tools.
There's a related issue with CLOS and vs C++ and Java style OO systems
which could also be interesting, which is that I think it's too hard
in CLOS to say who is allowed to see what bits of what. C++ and Java
spend all this time stressing about who is a friend of what & which
slots are visible & not, but in CLOS there's no support for this at
all, outside the package system, which is way too coarse. That's very
much in the Lisp style of course, but it would be cool to have a
standard way of being able to control, or document at least, things
like slot visibility. OTOH it's extremely hard to get this right I
think in a system as powerful as CLOS -- at least I spent quite some
time thinking about it and didn't get far.
[Note I sent this to comp.lang.clos as well]
--tim
KM> It is possible to have both fast dispatch in CLOS and support
KM> its other advanced features. I have built such an
KM> implementation. It is outlined in my "Under the Hood" JOOP
KM> column, which month/year I can't remember (ask the Franz sales
KM> people in...@franz.com for a reprint).
This all sounds quite impressive, and would probably solve some
performance issues in the kind of systems I'm currently involved with
-- event-driven simulations, where the event-dispatch time (mostly
done via generic functions) is quite often the limiting factor.
Currently I'm using Common Lisp to prototype new designs for parts of
the system, whereas the main implementation is still in C++ (legacy
code). But as the point of a total rewrite is getting nearer (prior
bad design decissions and quick-coding together with changing
requirements have made it necessary), I'm still considering using CL
for the whole thing (performance is an issue here, with
simulation runs between 2h and 2 days, but so are flexibility,
maintainability, personal resources, etc. which of course sounds quite
a lot like CL).
So, of course my question would be whether your implementation is
already included in one of Allegro's products? And if not, when (if
ever) will this be available for the end user? Sorry to sound
impatient, but I'm quite eager to test this in my problem domain ;)
Regs, Pierre.
I think that it's really not suitable unfortunately. I often have
whole collections of related classes providing bits of various
functionality. Although I use packages to define what functionality
is exported by these classes, it's way too heavyweight to deal with
the issues of who is meant to be able to see what in the
implementation. If I used it for that I'd end up with more package
infrastructure than code. And making even quite small mistakes with
packages tends to leave you with weird naming problems which are most
easily resolved by restarting Lisp and losing all your work...
--tim
Maybe large systems do not require large teams of programmers,
if you are using Lisp.
This sounds like an interesting possibility, but what are the ratios
and what is the evidence. Sadly there is a real lack of objective
studies showing anything one way or the other...
/Jon
--
Jon Anthony
OMI, Belmont, MA 02178, 617.484.3383
"Nightmares - Ha! The way my life's been going lately,
Who'd notice?" -- Londo Mollari
The above issue is the problem that the package system solves. If you're
using a third-party GUI library, all its internal functions and slots will
be in its own package. Meanwhile, any method and slot names you define
will be in the package you created for your application. So there
shouldn't be any name conflicts.
Even if you IMPORT from the GUI package, there shouldn't be a problem.
IMPORT only inherits the external symbols, and the package should only
export the symbols that are part of the documented interface.
>I'd like to hear some comments from people who've had more experience working
>with CLOS on a large project or with second party libraries, on why this isn't
>considered to be a problem.
There's no magic. You *do* have to think clearly about which methods
are meant to be internal etc. You are asking about _inadvertant_ name
collisions, right? For this, using internal symbols in the package
has worked for me. (There's also defun-in-flavor and in CLOS
generic-flet/labels).
In practice, the collision of slot/instance-variable names in mixins
has been more of a problem for me --- the mixins are usually within
the same package. This problem is solved by self-discpline,
organization, etc or very restricted use of mixins --- it's only an
issue inside the package, which presumably is only being developed by
co-operating designers.
Michael Greenwald
Gree...@cs.stanford.edu
> I'd like to hear some comments from people who've had more experience working
> with CLOS on a large project or with second party libraries, on why this isn't
> considered to be a problem. I've done fine with CLOS when I wrote all the code
> for my project, because I'm aware of what I put in the base classes, but I've
> never had the opportunity to use, say, a CLOS GUI library.
When you share code with other programmers, it may help if you can
document the classes that you define. When you use somebody else's
classes, you can then read their documentation.
I'm not sure if this problem is specific to CLOS. The question is one
of discipline, but this can be self discipline, imposed by the
programmes concerned, or it can be imposed by the language itself.
I've had access to a CLOS GUI library for a while now (in ACL/PC), and
now have another (in LispWorks). I've yet to do much with them, but
that's mainly because my code tends not to have a user interface.
CAPI doesn't look hard to use, apart from being completely different
to what I'm used to. I have more trouble with the interface builder
than the classes. However, that's because I'm used to a very different
style of interface building, where the exact details of the user
interface are edited using drag 'n' drop, with a one to one
relationship between the "high level" components and the native OS
components. CAPI doesn't work that way, for obvious reasons.
Documentation is also vital. You can never have enough example uses of
a framework, and no example will be too trivial. One small tip: don't
let the people who designed a class framework write the docs. Unless
they're exceptionally good at writing for other people, they'll be too
close to the software to see it from the perspective of somebody who
is unfamiliar with it. A reference manual is great, but you may also
need a good tutorial, too. At the very least, it'll save time.
--
<URL:http://www.wildcard.demon.co.uk/> You can never browse enough
"As you read this: Am I dead yet?" - Rudy Rucker
Please note: my email address is gubbish
Perhaps my comment lost something in its subtlety.
I think you will find that most of the successful applications
of Lisp have been in large complex systems that were deemed
"impossible" to write using the other languages with large
teams of programmers.
I am thinking offhand of operating systems, "AI" systems in myriad
domains (fraud detection, scheduling and planning for spacecraft,
commercial aircraft, military deployment), and telephone switching.
There are many examples.
However, these people were generally interested in solving a real-world
critical business problem, not explaining themselves to the outside world
or trying to conduct academic studies.
Just because you are not familiar with the "objective evidence" is not
a good reason to suppose it does not exist. I would suggest contacting
the marketing departments of Harlequin and Franz to see if they have
some material, and some references for you to talk with.
Actually, I don't expect that "you will find" anything, because I
don't really think you're interested. I think you're just trolling.
> > > Maybe large systems do not require large teams of programmers,
> > > if you are using Lisp.
> >
> > This sounds like an interesting possibility, but what are the ratios
> > and what is the evidence. Sadly there is a real lack of objective
> > studies showing anything one way or the other...
>
> Perhaps my comment lost something in its subtlety.
>
> I think you will find that most of the successful applications
> of Lisp have been in large complex systems that were deemed
> "impossible" to write using the other languages with large
> teams of programmers.
>
> I am thinking offhand of operating systems, "AI" systems in myriad
> domains (fraud detection, scheduling and planning for spacecraft,
> commercial aircraft, military deployment), and telephone switching.
> There are many examples.
Well, maybe. But having been involved in a couple of these, I find
myself skeptical for reasons that I directly observed in practice. Of
course, maybe those were simply badly done - that's certainly
plausible.
> Just because you are not familiar with the "objective evidence" is not
> a good reason to suppose it does not exist. I would suggest contacting
I'm speaking of true controlled studies - not the usual anecdotes.
And yes, I realize that (at least AFAIK) these don't really exist,
which was what I meant above.
> Actually, I don't expect that "you will find" anything, because I
> don't really think you're interested. I think you're just trolling.
Ouch. No not trolling. Hey, we actually _use_ CL here in conjunction
with research and commercial work.
I'm curious -- has anybody ever done any controlled studies on, say,
building an oil rig two different ways just to compare the results and the
costs and junk one of them? or building a railroad across the country only
to discard the one that didn't "win" by some arbitrary set of standards?
why would we expect the same of multimillion-line software projects? how
could it _not_ be anecdotal evidence? it's obvious to me that people won't
be "just testing" something as big as what we're discussing here.
#\Erik
--
404 You're better off without that file. Trust me.
> In article <uu3g8u...@pilgrim.com> Christopher Stacy <cst...@pilgrim.com> writes:
>
> > Maybe large systems do not require large teams of programmers,
> > if you are using Lisp.
>
> This sounds like an interesting possibility, but what are the ratios
> and what is the evidence. Sadly there is a real lack of objective
> studies showing anything one way or the other...
That would be interesting indeed. I think there *is* a difference
between the "Software engineering" approach and the "True hacker"
approach to a complex problem.
The software engineers would divide the problem into small managable
pieces, and design interfaces between the subproblems, a software
engineering process, quality measuring methods, and eventually, a lot
of people start working each on a small piece of the original problem.
While the True hacker would start by creating necessary
infrastructure, programming languages, other tools, whatever is needed
to make the complete problem managable. The idea is to create enough
of abstractions, to make the original problem managable to solve, for
a single or a small group of hackers.
I guess that the advantages of the Software engineering approach is
that it is easier to control and manage, and requires more people but
less genius for its success.
Objective studies would be interesting, but I would find also
subjective experiences of how complex problems are handled in the
practice.
/Niels
: With a language like C++ or Java, I only need to look at the public parts of the
: parent classes to make sure I'm not defining any new methods with the same name
: as an existing one unintentionally. Often this just means reading the
: documentation that comes with the library.
: With CLOS, I literally have to read all the code for all the base classes to make
: sure I'm not accidentally changing the behaviour of code in the base classes.
: It's as if, in C++, I declared every member function public and virtual. If I
: were to get, say, a CLOS GUI library, and wanted to create my own widget by
: deriving from an existing widget class, this could be a nightmare. I'd always
: have to make sure I'm not using the same name for a method or slot that one of
: the base classes has but which was intended to be private, and so wasn't
: documented.
I don't follow you at all here. The GINA GUI system from GMD is based
on CLOS and uses CLM (a kind of CLX for Motif) and writing a GUI with
it precisely requires what you seem to fear so much: You *must*
define own classes that specialize the predefined shell, window and
widget etc. classes, and you probably *want* to specialize methods
like popup, move, draw etc. And you are not destroying anything by
doing so.
The core GINA code lives in its own package -- that's probably the
answer to your fears. You're not addressing a virtual function or
whatever problem, but merely a name-space or visibility one.
I'm sure none of the CLOS-based GUI systems exhibits the problems that
you describe.
Regards,
Jo"rg Ho"hle.
Joerg....@gmd.de http://zeus.gmd.de/~hoehle/amiga.html
Is your objection an objection to the :: syntax that bypasses all
barriers? This is easily grepped for in the sources, even without
any fancy tools.
Nor do I see that discouraging :: is a "B&D" development process, any
more than discouraging "goto" is. If you've got a rogue hacker (using
one of the more negative senses of the word) out using his or her own
unintelligible style and violating project standards, you're in trouble
no matter what language you're using. (Fortunately, in Lisp you
tend to need fewer people, so the chances of winding up with such a rogue
are smaller.)
I think that Common Lisp indeed has features that limit the practical
size of a CL project. On the cdr hand, though, so do all languages
that I'm aware of, and the general experience is that overly large
projects don't work anyway. Barring actual data, I'd suspect that
a tailorable language like Lisp would do a lot better on large projects
than a non-tailorable language like C (or, for the perverse, COBOL).
C++ is very tailorable for a C-type language, but has its own problems.
David Thornley
this would be very disturbing if it was true, and you had me very nervous
that I had overlooked something this important, but after a careful review
of the CLOS specification, I believe you have missed the point that you
cannot modify the behavior of functions unless you redefine them, and you
wouldn't do that when you have generic functions that would call the method
specialized on the types of the actual arguments. thus, if you define a
generic function `foo', the generic function is independent of any classes,
but you would specialize a method for an object of class `C', which could
be the superclass for some subclass `S'. when the time comes to specialize
`foo' for objects of class `S', you just do it, and when `foo' is applied
to an object of class `S', it will call this new method. until then, you
would call the method for `C', but applied to an object of class `C', you
would still call the method you defined first. from within the method for
`S', you can still call the method for `C' with (call-next-method).
(defgeneric foo (x))
(defmethod foo ((x C))
...)
(defmethod foo ((x S))
...)
so either I misunderstood you or you have missed something I think you
should take another look at.
Actually, I believe that McDermott, which builds oil rigs, DID do a
study comparing the costs and quality of results when DESIGNING an oil
rig using the ICAD System (in Lisp) versus other means. Their corporate
conclusion was that that the methodology was superior and they developed
a clone of the ICAD System (also written in Lisp) for doing future
designs. They also sold their clone to other engineering companies
under the name WISDOM. Today, both ICAD and WISDOM are owned and sold
by Concentra (which I don't work for, but used to. Of course, I don't
speak for either McDermot or Concentra.)
Anyway, all this points out a couple of things.
1. Such internal business decisions are often occompanied by
cost/quality studies, but the methodoly and quality of the STUDY often
varies, and in the end, some of the actual corporate actions may have as
much to do with politics than with good research. When the studies are
done, they are usually used internally only, because they necessarily
carry proprietary information, such as how much engineering time goes
into the various activities associated with designing an oil rig.
Nonetheless, you might see articles in trade publications associated
with the field. I am not personally aware of which publications one
should search through for this study. Perhaps ones associated with
offshore enginnering, steel construction, energy production....
2. The really big wins don't come from using Lisp directly, but from the
specialized systems and languages which one can easily create with Lisp.
Therefore, in addition to looking to the sales departments of Franz and
Harlequin, one should look to the sales departments of companies making
Lisp based products such as:
Autodesk (autocad)
Interleaf
Concentra (ICAD)
Cognition
Oracle
Nichiman graphics (games)
Gensym
etc.
In all cases, your not likely to find much mention of Lisp itself, nor
will it be easy to apportion the success between the qualities of Lisp
and the other factors involved.
or building a railroad across the country only
> to discard the one that didn't "win" by some arbitrary set of standards?
>
> why would we expect the same of multimillion-line software projects? how
> could it _not_ be anecdotal evidence? it's obvious to me that people won't
> be "just testing" something as big as what we're discussing here.
>
In article <30820518...@naggum.no> Erik Naggum <cle...@naggum.no> writes:
> * Jon S. Anthony
> | I'm speaking of true controlled studies - not the usual anecdotes.
>
> I'm curious -- has anybody ever done any controlled studies on, say,
> building an oil rig two different ways just to compare the results and the
> costs and junk one of them? or building a railroad across the country only
> to discard the one that didn't "win" by some arbitrary set of standards?
Good point.
> why would we expect the same of multimillion-line software projects? how
> could it _not_ be anecdotal evidence? it's obvious to me that people won't
> be "just testing" something as big as what we're discussing here.
Yes, I agree with you here as well. I'm pretty sure that this is the
basic reason why such "studies" are very rare (though there have been
a couple). So, maybe you are correct - anecdotal is about as good as
it is going to get and we just have to use that + our own experiences
+ some "reasonable" judgement.
This may not be much help, but I gather the book called something like
The Mythical Man-Month discusses at length the problem of throwing more
people at a project--you know, doubling the staff doesn't halve the
development time.
I didn't read it because I already agreed with the title <g>.
Would a good example be the original Mac OS vs Windows? The former was
developed by a relatively small group, no?
Programming is tough, as we all know. C++ and its strait-jacketed ilk
try to save us from ourselves by limiting us. Lisp has the philosophy
(not that McCarthy intended it, tho) that if you make a language an
order of magnitude more powerful (by /not/ limiting the programmer)
we'll be able to endure a few shots to the foot. <g>
And as others have noted, the package system /does/ offer some of the
hiding the original poster likes in C++.
Cheers,
Ken
Slots are named using symbols, and the package system catalogs symbols, so
the package system protects you there as well.
What you may be thinking of is initialization arguments, which are often
named using a symbol in the keyword package with the same name as the
slot. Here's an example:
(in-class gui)
(export '(parent x))
(defclass parent () ((x :initarg :x :accessor x)))
(in-class application)
(declass child (gui:parent) ((x :initarg :x :accessor x)))
In this case, the CHILD class has two slots, one named PARENT:X, the other
named CHILD::X. Code in the CHILD class can access them by calling
(parent:x ...) and (x ...). The collision is in a call to MAKE-INSTANCE;
(make-instance 'child :x 3) will set *both* slots to 3.
It was this very issue that prompted us to liberalize "keyword arguments"
so they don't have to be in the KEYWORD package; see p.79 of CLtL2, and the
examples of :INITARG on p.806. Thus, the safe way to code the above
classes is:
(in-class gui)
(export '(parent x))
(defclass parent () ((x :initarg x :accessor x)))
(in-class application)
(declass child (gui:parent) ((x :initarg x :accessor x)))
Now you can use (make-instance 'child 'x 3 'parent:x 5). Esthetically it's
not as nice, since you don't have the distinctive :prefix notation, but it
solves the namespace collision problem.
Packages aren't a perfect module system (there's a good reason why both
CLtL and the ANSI spec document it in Chapter 11). In particular, since
works at the symbol level, if a symbol names multiple things (e.g. a
function, a variable, and a slot) there's no way to export/import just one
of those things. An example of this came up in the newsgroup a few months
ago: someone wanted to shadow the arithmetic functions (+, *, /, -) in his
package, but this also ended up shadowing the variables that the Lisp
top-level loop uses to hold the previous form, the previous result, the
previous results as a list, and the current form -- when in that package,
you have to reference lisp:* and so on.
--
Barry Margolin, bar...@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
From: ajen...@cs.umass.edu (Adam P. Jenkins)
Newsgroups: comp.lang.lisp,comp.lang.clos
Date: 05 Sep 1997 16:47:53 -0400
Organization: University of Massachusetts, Amherst
Lines: 91
Sender: ajen...@wagga.cs.umass.edu
NNTP-Posting-Host: wagga.cs.umass.edu
X-Newsreader: Gnus v5.4.52/XEmacs 20.2
Xref: agate comp.lang.lisp:30535 comp.lang.clos:4477
...
A little of both :) The ingredient I was missing was the package
system. Without using the package system in conjunction with the
object system, the problem I described would indeed be real. Also,
note that I'm talking about *unintentionally* overriding a method -- I
can accept that CLOS doesn't have features to prevent intentional
modification of methods or slots.
Here's an example of accidentally changing the behaviour of a public
method. Suppose class B is a subclass of A
A
|
B
(defmethod m ((arg A))
; This is a method intended to be private to A, so not documented
; externally
)
(defmethod n ((arg A))
; this is a publicly documented method on A
(m arg))
(defmethod m ((arg B))
; This method has nothing to do the m for A, I just didn't know
; m for A existed
)
(setq a-b (make-instance 'B))
(n a-b) ; in this case m for B will be called, which is not what was intended.
In this case, the first defmethod for m created a new generic function
called m, and added a method to it. The second defmethod for m added
a new method to the generic function m, which is not what I had
intended. If the first defmethod were placed in its own package, then
the second defmethod for m would have created a different generic
function, and the problem would be solved.
You are still missing the package system in your picture.
A better rewriting of your code would be
------------------------------------------------------------------------------
(defpackage "LIB" (:use "COMMON-LISP)
(:export "A" "B" "N"))
(in-package "LIB")
(defclass A () ())
(defclass B () ())
(defmethod n ((arg A))
(m arg))
(defmethod m ((arg A)) #|do stuff|#)
------------------------------------------------------------------------------
Now you have your "LIB" package. If this is advertised as such, you'd
better not work within it. Therefore, in another package....
------------------------------------------------------------------------------
(defpackage "APPLICATION" (:use "COMMON-LISP")
)
(in-package "APPLICATION")
(setf a-b (make-instance 'LIB:B))
(defmethod m ((arg LIB:B))
(format t "I do not override LIB:M on LIB:B~%"))
------------------------------------------------------------------------------
So back to my GUI example, as long as the widget class was in its own
package, and only exported the methods that were supposed to be
public, then I wouldn't accidentally change the behaviour of the
widget code when I derived my own button-widget class.
However this still doesn't address the problem of accidentally
overriding slots. If in my button-widget class I add a new slot
called x, it could override an existing slot called x in the widget
class, which could be completely unrelated. As far as I know the
package system can't protect me from this, so I'm back again to having
to check if any of the superclasses have a slot called x before I add
one. Or worse, if the librarary implementor adds the slot called x
after I write my code. Is there a way to make the package system hide
certain slots?
Yes. You use the :accessor, :reader and :writer methods. The rest is
as per the example before.
I feel compelled to point these problems out because some people seem
to think that C++'s private and protected tags are solely to prevent
others from intentionally going against the class author's
intentions. I've always thought of them as a way to get the compiler
to help me prevent errors, just like the package system in Lisp does.
I personally spend most of my time working with libraries to which I
have the source code, so I could just modify the library if that's
really what I wanted to do.
That is your specific problem. The C++ and Java
public/protected/public scheme is a good thing, but personally I have
not felt its absence in CL.
Cheers
That should be (in-package gui), of course.
A little of both :) The ingredient I was missing was the package
A
|
B
So back to my GUI example, as long as the widget class was in its own
package, and only exported the methods that were supposed to be
public, then I wouldn't accidentally change the behaviour of the
widget code when I derived my own button-widget class.
However this still doesn't address the problem of accidentally
overriding slots. If in my button-widget class I add a new slot
called x, it could override an existing slot called x in the widget
class, which could be completely unrelated. As far as I know the
package system can't protect me from this, so I'm back again to having
to check if any of the superclasses have a slot called x before I add
one. Or worse, if the librarary implementor adds the slot called x
after I write my code. Is there a way to make the package system hide
certain slots?
I feel compelled to point these problems out because some people seem
to think that C++'s private and protected tags are solely to prevent
others from intentionally going against the class author's
intentions. I've always thought of them as a way to get the compiler
to help me prevent errors, just like the package system in Lisp does.
I personally spend most of my time working with libraries to which I
have the source code, so I could just modify the library if that's
really what I wanted to do.
--
Adam P. Jenkins Office Phone: (413) 545-3059
mailto:ajen...@cs.umass.edu
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.2
mQBNAzBt18UAAAECALmLVRW6EP59OrypUB4uT6ADRYGB3FfyW1wACf40cvdTfOUy
QHhM8Oy4aMWZ5RNYeY5qTkCRnvtsfGSsLRxEmN0ABRG0KUFkYW0gUC4gSmVua2lu
cyA8YXBqQHR3YWluLm9pdC51bWFzcy5lZHU+
=yzSE
-----END PGP PUBLIC KEY BLOCK-----
* Jon S. Anthony
| I'm speaking of true controlled studies - not the usual anecdotes.
I'm curious -- has anybody ever done any controlled studies on, say,
building an oil rig two different ways just to compare the results and the
costs and junk one of them? or building a railroad across the country only
to discard the one that didn't "win" by some arbitrary set of standards?
why would we expect the same of multimillion-line software projects? how
could it _not_ be anecdotal evidence? it's obvious to me that people won't
be "just testing" something as big as what we're discussing here.
#\Erik