This is the mail archive of the guile@sourceware.cygnus.com mailing list for the Guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Some questions about GOOPS and CLOS in general


Craig Brozefsky <craig@red-bean.com> writes:

> Mikael Djurfeldt <mdj@thalamus.nada.kth.se> writes:
> 
> > What is your concern with having accessors/getters for all slots?  Why
> > is that a problem.  (Actually, in the example code I showed, most
> > slots had accessors.)
> 
> Because I use the accessor/no-accessor distinction on slots to
> indicate what slots are supposed to be part of the interface for that
> class.  When I export to other modules I can choose to not export the
> accessors for various slots, but when working in that module I
> obviously cannot use the module system to help me define the class
> interfaces.  In other words, within the module that a class is
> defined, I use the presence and lack of accessors for a slot as a way
> of defining the interface for that class for code that is in that same
> module.  I think this makes it easier for others to add patches and
> modify the module.

Hmm...  I see what you mean.  But to me, it feels a little strange
that (x o) <-> (slot-ref o 'x) should represent this distinction.

I really think it is unfortunate that there are two such radically
different syntaxes for doing the same thing.  And the "locality" vs
"publicity" doesn't really show in the syntax.  (There is a similarity
with C++: using an accessor vs using the variable directly.  But this
doesn't transfer to Scheme.  In a way, `slot-ref' is even more
"distant" from the slot than an accessor. [object x name --> value] vs
[object --> value])

Also, the use of `slot-ref' easily gives unreadable code, compare:

  (set! (d o) (sqrt (+ (x o) (y o))))

with

  (slot-set! o 'd (sqrt (+ (slot-ref o 'x) (slot-ref o 'y))))

`slot-ref' doesn't give much extra useful information in the syntax,
but it does decrease readability.

(BTW, I will add the macros `with-accessors' and `with-slots' (if
 we're going to keep slot names) soon.  They will add a new
 possibility of writing readable code.)

Maybe one can divide this problem into two parts:

1. Name-space cluttering.

2. Distinguishing slots belonging to the class interface from slots
   belonging to the implementation.


As I've said earlier, the basic idea of the suggested change is to
leave all administration of names to the module system.  Slots have
accessor (generic) functions (or getters and/or setters) as the only
means of access.

Problem 1:

  These accessor functions will share the name-space with other
  functions and variables in the module, which means that the
  programmer has to make sure not to name a slot accessor the same as
  a function or a variable in the module.  We also probably want to
  allow multiple class definitions within one module which could lead
  to trouble.

Let's start with multiple classes in one module.  Classes C1 and C2
share one module.  They represent different kinds of objects, but both
have a slot `length'.  Although C1 and C2 are different types of
objects, slot 7 in C1 and slot 13 in C2 happen both to be named
`length'.  Why?  Because both slots represent the same aspect of the
object: the length.

The code will look like this:

  (define-module (M)
    ...)

  (define C1 (...)
    ...
    (length ...)
    ...)

  (define C2 (...)
    ...
    (length ...)
    ...)

The effect of this code in GOOPS is that there will be one generic
function `length' with two methods, one specialized to each class.

To me this seems OK: Since both slots represents the same aspect or
concept, both methods are collected in the same generic function.  If
the slots don't represent the same aspect or concept, it would in most
cases be a good idea to consider different names anyway.

slot-ref/set! would indeed give us name spaces "private" to each
class, but note that this only solves one half of the problem: It is
still necessary to think about proper naming for public accessors.
I'm not too happy about needing to think differently for "private" and
"public" slots.

Also, note the nature of the record interface in SRFI-9.  Just as the
corresponding procedural interface (used by Guile and slib, for
example), slots are accessed using accessor functions.  Here we don't
have the freedom of genericity.  These functions can be used only for
one type of objects.

With a module system, and sensible naming, I don't think this is a
problem.  But, I have to confess that I don't have experience of
building really big systems with either CLOS, STk or GOOPS.  What I
*have* noticed is that I tend to create accessors for almost all
slots...


Moving to the second problem, it is important to distinguish between
parts of the library which constitute the interface and parts which
belong to the implementation.  Parts which only belong to the
implementation may change without notice.

Problem 2:

  There isn't any mechanism in the suggested, modified, GOOPS to
  prevent access to "private" parts of a class from other code in the
  same module.  In current GOOPS you can purposely neglect to generate
  accessors for these private parts.  When accessing the private parts,
  you are reminded that they are private because you have to use
  slot-ref/set!.  E.g., class C1 above may contain a slot with
  accessor x which isn't published in the interface of the module.
  The programmer of C1 didn't actually intend that anybody should use
  x, but the programmer of C2 (in the same module) uses x without
  thinking.  The programmer of C1 later removes x and code for C2
  crashes...

Let's focus on the words ""private" parts of a class from *other*
code".  What does that mean?  Well, there are obviously different
groups of functions in the module.  A class can belong to such a
group, and the other group isn't supposed to poke around with the
unpublished parts of the other.

To me this suggests that there should be a module border between these
groups.  This is exactly what module borders are for.  I don't
understand what "class interface" would mean within a module.  (If one
really wishes to have different status of slots within one module, one
could always arrange the slots in groups in the class definition and
use comments like ;; Public:, ;; Private:.)  I think the module system
should support multiple modules per file.

I *do* understand the need to limit access to a subset of the slots of
a class within the API.  But this is effectuated by only publishing
those accessors which belong to the API.  The only way to bypass this
border is to use mechanisms in the module system to peek into other
modules (similar in spirit to the current module system's `local-ref'
and `nested-ref').  I think that flexibility is precisely good
enough.

> > > Also, how would I specialize the accessors in this to do more than
> > > just set the value or get the value?  What if I wanted them to do
> > > some transformation on the value?
> > 
> > The most obvious solution would be this:
> > 
> >   (define-class <c> ()
> >     (x' ...))
> > 
> >   (define-accessor x)
> >   (define-method x ((o <c>))
> >     ... do something ...
> >     (x' o))
> 
> With that solution I have to tweak my slot names, something I'd really
> rather not do.  At that point you might as well just tweak them for
> me, defining an accessor "slotname'" and a method "slotname".  I could
> redefine the "slotname" method.  But doing that just seems ludicrous.

Actually, if we only had had accessors to start with, and someone came
and suggested that the way to solve this problem was to divide slot
access into two levels, "high level" (x o) and "low level"
(slot-ref o 'x), I would regard that as pretty ludicrous as well...  :)

> > You can also use the MOP (compute-get-n-set) or the metaclass
> > <active-class> defined in active-slot.scm in the GOOPS distribution.
> 
> Yarch!

Hmm...  What are you reacting to?  You shouldn't care about the
implementation (which isn't too nice).  The important thing is the
interface, e.g.:

(use-modules (oop goops) (oop goops active-slot))

(define-class c ()
  (x #:allocation #:active
     #:before-slot-ref (lambda (o)
                         (display "x accessed!\n")
                         #t ;grant access to slot
                         ))
  #:metaclass <active-class>)

With some rewriting of the metaclass and the addition of a custom
define-class macro it could become:

(define-active-class c ()
  (x #:before-slot-ref (lambda (o)
                         (display "x accessed!\n"))))

which I think is pretty decent.


[...]

> The idea you posted shortly after making this post where you added a
> #:name option just seems to re-introduce the name, but makes it
> optional, which means that I cannot rely on it being their when I'm
> building my reflection tools, like SQL table schema generators and
> the like.  I think it very useful to have names to identify slots in
> a class, and that they should be mandatory (at least in the standard
> slot defs).

But do really all objects require slot names?  Those objects which you
are going to use in the data base could have a common superclass
<database-object> with a metaclass <class-with-slot-names>.

But, still, maybe debugging is reason enough to keep the names... (but
not slot-ref/set!).

> Or any other type of reflective programming task.  It seems like we're
> throwing away a useful bit of information for a kinda dumb reason (a
> limitation in our present reader).

Hmm..  Could you develop this a bit?  I presume that you're referring
to the CLOS package system (package:[:]symbol).  But how could that
help here?

> Thanx for your response Mikael, and all of your work so far, please
> take my disagreement as freindly cooperation to make GOOPS as good as
> possible, and not griping about your work.

Absolutely.  And I really do appreciate it, especially from someone
with some experience of real-world OO programming.  And the same goes
both ways, I hope.  I can be pretty hard in my comments sometimes, but
it's always with good intent, at least I hope so.  :)


(254 lines... well, well, will *anybody* read this? 8-)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]