Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Object Oriented Omissions

71 views
Skip to first unread message

TonyW

unread,
Feb 16, 2005, 10:45:46 AM2/16/05
to
Hi all,

I'm presently learning Lisp and I'm puzzled by what appear to be significant
omissions in the capabilities of CLOS.

Specifically, I'm concerned with:

1. The lack of access specifiers. In particular, there seems to be no easy
way of making slots private, so CLOS does not support what I understood to
be a basic OO capability of hiding implementation, and

2. The lack of a constructor. I can always define a function to act as a
constructor, but then I have to remember to call it explicitly.

Is anyone able to explain the reasons behind these omissions? Also, does
anyone have suggestions as to how to go about adding these capabilities?

As I am still in the process of learning Lisp, restricting yourself to the
Lispish equivalent of monosyllables would be appreciated :)

Thankyou.

Tony


Peder O. Klingenberg

unread,
Feb 16, 2005, 10:56:27 AM2/16/05
to
"TonyW" <talkt...@email.com> writes:

> 1. The lack of access specifiers. In particular, there seems to be no easy
> way of making slots private, so CLOS does not support what I understood to
> be a basic OO capability of hiding implementation, and

CLOS deals with class inheritance and the behaviour of objects through
methods and generic functions. Information hiding is an orthogonal
concept, and is dealt with through packages. If something is private,
don't export it from your package.

> 2. The lack of a constructor. I can always define a function to act as a
> constructor, but then I have to remember to call it explicitly.

You can define constructors as specializations on the standard generic
functions initialize-instance or shared-initialize, which are called
by make-instance.

...Peder...
--
I wish a new life awaited _me_ in some off-world colony.

Pascal Bourguignon

unread,
Feb 16, 2005, 11:08:32 AM2/16/05
to
"TonyW" <talkt...@email.com> writes:

> Hi all,
>
> I'm presently learning Lisp and I'm puzzled by what appear to be significant
> omissions in the capabilities of CLOS.
>
> Specifically, I'm concerned with:
>
> 1. The lack of access specifiers. In particular, there seems to be no easy
> way of making slots private, so CLOS does not support what I understood to
> be a basic OO capability of hiding implementation, and

(defpackage :demo
(:export :demo :public-slot))
(in-package :demo)

(defclass demo ()
((private-slot)
(protected-slot :accessor protected-slot)
(public-slot :accessor public-slot)))

(setf (public-slot (make-instance 'demo)) :good) --> :good
(setf (protected-slot (make-instance 'demo)) :good) --> :good
(setf (private-slot (make-instance 'demo)) :bad) --> error

(in-package :common-lisp-user)
(use-package :demo)

(setf (public-slot (make-instance 'demo)) :good) --> :good
(setf (protected-slot (make-instance 'demo)) :bad) --> error
(setf (private-slot (make-instance 'demo)) :bad) --> error

You can even distinguish between no-access, read-only, write-only and
read-write with :reader and :writer instead of :accessor.


> 2. The lack of a constructor. I can always define a function to act as a
> constructor, but then I have to remember to call it explicitly.

Yes, that's because you have three different constructors that you
cannot see any one.

INITIALIZE-INSTANCE
SHARED-INITIALIZE
REINITIALIZE-INSTANCE

> Is anyone able to explain the reasons behind these omissions?

Perhaps you need to read more attentively the CLHS:

http://www.lispworks.com/reference/HyperSpec/


> Also, does anyone have suggestions as to how to go about adding
> these capabilities?

Mu.

--
__Pascal Bourguignon__ http://www.informatimago.com/
Until real software engineering is developed, the next best practice
is to develop with a dynamic system that has extreme late binding in
all aspects. The first system to really do this in an important way
is Lisp. -- Alan Kay

Marco Baringer

unread,
Feb 16, 2005, 11:20:30 AM2/16/05
to
"TonyW" <talkt...@email.com> writes:

> Hi all,
>
> I'm presently learning Lisp and I'm puzzled by what appear to be significant
> omissions in the capabilities of CLOS.

as usual, someone has already said it better then me, read the first
few paragraphs of http://www.tfeb.org/lisp/hax.html#ABSTRACT-CLASSES

> Specifically, I'm concerned with:
>
> 1. The lack of access specifiers. In particular, there seems to be no easy
> way of making slots private, so CLOS does not support what I understood to
> be a basic OO capability of hiding implementation, and

clos doesn't have the equivalent of private, public, protected,
friend, etc. these don't really make sense either since CLOS methods
are grouped around generic functions and not classes.

personally i think it's a mirage in any other language (there is,
afaik, no language where you can not get by pass these access
specifiers) so i'm ok with my oo system admitting the truth. i'm
pretty sure you could, with the proper macro vodoo (though the method
combination may be enough) create whatever kind of access scheme you
want.

of course, common lisp (not just clos) has a package mechanism which
allows you limit access to certain symbols (which are then used as
names of classes, methods, etc.), though this probably isn't what you
want either.

> 2. The lack of a constructor. I can always define a function to act as a
> constructor, but then I have to remember to call it explicitly.

the equivalent of 'new' is called make-instance, you customize
make-instance by defining :after methods (you can define primary,
:around or :before methods but :after is genarally what you want) on:

initialize-instance, shared-initialize and/or reinitialize-instance.

see http://www.lisp.org/HyperSpec/Body/sec_7-1.html for details.

> Is anyone able to explain the reasons behind these omissions? Also, does
> anyone have suggestions as to how to go about adding these capabilities?

before you go about attempting to saw off clos' legs so that it walks
like c++ or java i'd suggest studying CLOS and deciding if your time
is not better spent learning the many (and there are many) different
things CLOS can do. here's a list of strating points [i'm
intentionally not explaining what any of these are but expecting you
to use Google Juice(TM)]:

1) multi method dispatch

2) eql specializers

3) change-class

4) the class standard-class and the entire meta object protocol (aka MOP).

5) method combination

--
-Marco
Ring the bells that still can ring.
Forget the perfect offering.
There is a crack in everything.
That's how the light gets in.
-Leonard Cohen

Wade Humeniuk

unread,
Feb 16, 2005, 11:37:10 AM2/16/05
to
TonyW wrote:

> Is anyone able to explain the reasons behind these omissions? Also, does
> anyone have suggestions as to how to go about adding these capabilities?
>

If you miss contructors, you can do some straight up CLOS
coding.

(defgeneric constructor (obj &rest initargs &key &allow-other-keys))

(defclass constructable-class () ())

(defmethod initialize-instance :after ((obj constructable-class) &rest initargs &key
&allow-other-keys)
(apply #'constructor obj initargs))

(defclass circle (constructable-class)
((radius :initarg :radius :accessor radius)
(area :accessor area)))

(defmethod constructor ((obj circle) &key radius &allow-other-keys)
(setf (area obj) (* pi radius radius)))

Saves some thinking about initialize-instance ... but it does not
buy you much.


CL-USER 4 > (defparameter circ (make-instance 'circle :radius 1.23))
CIRC

CL-USER 5 > (area circ)
4.752915525615998

CL-USER 6 > (describe circ)

#<CIRCLE 2068E70C> is a CIRCLE
RADIUS 1.23
AREA 4.752915525615998

CL-USER 7 >

Wade

Kaz Kylheku

unread,
Feb 16, 2005, 12:10:26 PM2/16/05
to
TonyW wrote:
> Hi all,
>
> I'm presently learning Lisp and I'm puzzled by what appear to be
significant
> omissions in the capabilities of CLOS.
>
> Specifically, I'm concerned with:
>
> 1. The lack of access specifiers. In particular, there seems to be
no easy
> way of making slots private, so CLOS does not support what I
understood to
> be a basic OO capability of hiding implementation, and

Lisp has symbols as first-class entities, and a package system which
provides a framework for organizing symbols, and rules for resolving
identifiers in Lisp text into internal symbols.

When Lisp text is scanned, identifiers that denote symbols are subject
to access and lookup rules dictated by the package system, and are
converted to an object in memory. From then on, the symbol is a pointer
to that object and no longer a piece of text. These lookup and access
rules are done without reference to the object system at all. The
package system works with all symbols, regardless of what entities
those symbols denote.

It is these symbol objects which are then used to name entities in the
program, such as functions, macros, classes and their slots.

Because the package system already provides an adequate mechanism for
separating namespaces within the program to guard against collisions,
and for guarding against unwanted internal dependencies, there is no
need to have a duplicate mechanism at the other levels, where symbols
denote class slots and other entities.

Note that Lisp has no concept of a class scope to begin with. The body
of a method is in an ordinary lexical scope which is not ``within'' the
class. Expressions in that body are not treated specially, with respect
to that class.

But, every piece of Lisp text is read (lexically scanned) in the
context of some package.

Languages like Java and C++ don't have a first-class symbol
abstraction. All of the drama of symbols and their resolution takes
place at compile time. There is no clear layer separation: classes play
a double role as symbol tables, giving rise to confusing name
resolution and access rules.

Lexical scanning and parsing are done at the same time, and so the C++
compiler can feed information between the two. When processing the
syntax of a class definition, for instance, it can open up a new symbol
table and tell the lexical analyzer to stick new identifiers in there,
and then pop that symbol table out when the closing curly brace is
encountered. The parsing decisions themselves can be made by referring
to symbol tables.

Lisp doesn't work that way at all. Its lexical analyzer (called the
reader) treats the program text as a notation for structured data. It
returns a data structure, which may then be treated as code. Its
actions are self-contained; it doesn't specially recognize function
bodies or classes or anything like that.

It's called layer separation! It's a good thing Stroustrup and Gosling
didn't design TCP/IP, because we'd probably have packets being routed
based on which part of the program called send(), yet
sliding-window-related negotations and path MTU discovery would be done
by the application.

> 2. The lack of a constructor. I can always define a function to act
as a
> constructor, but then I have to remember to call it explicitly.

Constructors are always called explicitly; not sure what you are
talking about there. For instance

MyClass foo = new MyClass(argument1, argument2); // Java

That is an explicit call.

Lisp is chock full of constructors everywhere. (It just doesn't have
the built-in concept of special functions that are named after a
class.)

For instance:

(list 1 2 3)

is a constructor call. Any time you are doing functional programming,
with its implicit memory management, there is a whole lot of
constructing going on. Nothing is ever overwritten; new results are
constantly constructed out of old ones.

The MAKE-INSTANCE function in Lisp is a constructor. You give it a
class and other arguments, and it transforms them into an instance of
that class.

You can wrap calls to MAKE-INSTANCE in your own functions. If you like,
you can name those functions after your classes. So if you have a class
POINT of two-dimensional points, you can make a two-argument function
POINT which makes instances of it.

Adrian Kubala

unread,
Feb 16, 2005, 11:44:38 AM2/16/05
to
TonyW <talkt...@email.com> schrieb:

> 1. The lack of access specifiers. In particular, there seems to be no
> easy way of making slots private, so CLOS does not support what I
> understood to be a basic OO capability of hiding implementation, and

Hiding implementation just means your API does not *expose* the
implementation. If the next version of Java ignored access specifiers,
would any existing programs work any differently? And yet they'd
suddenly be "not OO"?

> 2. The lack of a constructor. I can always define a function to act as a
> constructor, but then I have to remember to call it explicitly.

So in Java you type "Foo x = ;" and then the compiler reads your mind?

Peter Seibel

unread,
Feb 16, 2005, 1:04:11 PM2/16/05
to
"Kaz Kylheku" <k...@ashi.footprints.net> writes:

> The MAKE-INSTANCE function in Lisp is a constructor. You give it a
> class and other arguments, and it transforms them into an instance
> of that class.

While what Kaz says is true, that might be confusing to someone who
"speaks" Java or C++. Another way to phrase it is that MAKE-INSTANCE
is the entry point to the constructor (much like "new" in Java). The
backend of the constructor is provided (as others have noted) by
methods on INITIALIZE-INSTANCE (or SHARED-INITIALIZE). So if your
question is, "how to I ensure that certain code is always run when an
instance of my class is created, the answer is to add an :after method
to INITIALIZE-INSTANCE or SHARED-INITIALIZE.

-Peter

--
Peter Seibel pe...@javamonkey.com

Lisp is the red pill. -- John Fraser, comp.lang.lisp

Kalle Olavi Niemitalo

unread,
Feb 17, 2005, 2:02:36 AM2/17/05
to
Adrian Kubala <adr...@sixfingeredman.net> writes:

> If the next version of Java ignored access specifiers,
> would any existing programs work any differently?

Yes, they would. Here is an example:

class X {
private void f(long x) {
System.out.println("X.f(long)");
}
void g() {
h();
}
private void h() {
System.out.println("X.h()");
}
}

class Y extends X {
void f(float x) {
System.out.println("Y.f(float)");
}
void h() {
System.out.println("Y.h()");
}
static void main(String[] s)
{
Y y = new Y();
y.f(0);
y.g();
}
}

Currently, the output is:
Y.f(float)
X.h()

If you remove the "private" keywords, the output changes to:
X.f(long)
Y.h()

Incidentally, a similar change in a C++ program would not have
this effect: in that language, overriding and overloading work
independently of access specifiers. (That is not entirely
good though, as adding a private member to a base class can
then alter the behaviour of derived classes in unexpected ways.
There has been some discussion about changing this in a future
revision of the standard, but backward compatibility requires a
new syntax for the new semantics, and this gets clumsy because
C++ does not have a package system for its keywords.)

Adrian Kubala

unread,
Feb 17, 2005, 11:35:42 AM2/17/05
to
Kalle Olavi Niemitalo <k...@iki.fi> schrieb:

> Adrian Kubala <adr...@sixfingeredman.net> writes:
>> If the next version of Java ignored access specifiers,
>> would any existing programs work any differently?
>
> Yes, they would.

Wow, I did not know that. I hope the point came through anyways, that an
object-oriented design is still object-oriented even if people can
extend it more than you'd like. And what's OO about a method that can't
be overridden? It sounds like Java's excuse for an unexported function.

Steven E. Harris

unread,
Feb 17, 2005, 5:35:49 PM2/17/05
to
Kalle Olavi Niemitalo <k...@iki.fi> writes:

> That is not entirely good though, as adding a private member to a
> base class can then alter the behaviour of derived classes in
> unexpected ways.

Can you give an example of such a C++ program? Overload resolution
happens after name lookup, so in this case no addition to the base
class could change the behavior. That restriction is imposed by
design.

Furthermore, to compare the Java program to an equivalent in C++,
you'd have to specify whether these functions -- h() in particular --
were intended to be virtual or not.

--
Steven E. Harris

Kalle Olavi Niemitalo

unread,
Feb 26, 2005, 8:53:57 AM2/26/05
to
"Steven E. Harris" <s...@panix.com> writes:

> Kalle Olavi Niemitalo <k...@iki.fi> writes:
>
>> That is not entirely good though, as adding a private member to a
>> base class can then alter the behaviour of derived classes in
>> unexpected ways.
>
> Can you give an example of such a C++ program?

Jim Hyslop and Herb Sutter gave an example in their
"Conversations" column in the February 2004 issue of
C/C++ Users Journal. It was titled "Virtually Misbehavin':
C++ implicit overriding at work".

0 new messages