* SOS: Simple Object System
@ 2008-09-13 22:42 Maciek Godek
2008-09-14 10:22 ` Neil Jerram
` (2 more replies)
0 siblings, 3 replies; 16+ messages in thread
From: Maciek Godek @ 2008-09-13 22:42 UTC (permalink / raw)
To: guile-user
[-- Attachment #1: Type: text/plain, Size: 2266 bytes --]
Hi,
Using some hints you gave me, I've implemented a really tiny
object system -- and I would like to know your opinion ("why
it's still better to use goops" :D)
The notation for defining classes is following (using example of a sphere):
(define sphere
(class
'(x y radius)
'((move (dx dy)
(set! x (+ x dx))
(set! y (+ y dy)))
(scale (factor)
(set! radius (* factor radius))))))
To make an instance, you simply use instance-of:
(let ((S (instance-of sphere))) ... )
you can supply initial values to the object's props
and call it's methods
(let ((S (instance-of sphere 1 2 3)))
(in S '(move 1 1)) ;move the sphere from (1 2) to (2 3)
(in S '(scale 5))
(get S 'radius)) ; returns 15
The implementation is very simple: every object is
a vector. Its first field is always a reference to the class,
and the remaining fields are object's state (in the example,
the values of x, y and radius of S)
A class definition is a vector consisting of:
hashmap N from property names to vector indices (in object)
hashmap M from member function names to their indices in F
vector F of member functions
There's nothing surprising in here, and won't be. It has, as I can
tell, a few advantages over goops -- mainly, storing objects
as vectors allows for an efficient and convenient object treating
from C level, so boatmen should be satisfied.
Secondly, people accustomed to the object.method() notation
(like myself) won't feel lost and the global namespace will be
kept clean.
The system certainly isn't as complex as goops and doesn't
handle types (in general) so exquisitely. Also, it's unable to
support multiple inheritance efficiently (single inheritance isn't
supported as well, but this could be done quite easily if needed),
but that's not my point.
I just wanted to ask if you have any comments or remarks
to share with (I know I'm not the first guy to implement a thing
like this). I am currently using this system to implement another
system (for networked objects -- I wrote about it in another post)
and so far it caused no trouble.
I attach the working implementation (if anyone's interested).
For those who got this far (yes, that would be... you!), thanks for
your attention :D
M.
[-- Attachment #2: sos.scm --]
[-- Type: application/octet-stream, Size: 3845 bytes --]
(use-modules (srfi srfi-1))
(use-modules (srfi srfi-17))
(use-syntax (ice-9 syncase))
(define-syntax let-alias
(syntax-rules ()
((_ ((id alias) ...) body ...)
(let-syntax ((helper (syntax-rules ()
((_ id ...) (begin body ...)))))
(helper alias ...)))))
;; the `class' function creates a new class. `private' and `public' are lists
;; of symbols (variable names), and `member-functions' is a list of member function
;; definitions of a shape: (name (args) body).
(define (class member-vars member-functions)
(let ((definition (make-vector 3))
(properties (append '(class) member-vars))
;; symbols->hashed-indices transforms a list of symbols into a hash table
;; containing integers ranging from 0 to length(symbols)-1, for instance,
;; (symbols->hashed-indices '(a b c)) returns a hash map such that
;; for key 'a' the value is 0, for 'b' 1, for 'c' 2 in other words it
;; returns a hash map X such that for every symbol
;; (eq? (list-ref symbols (hash-ref X symbol)) symbol
(symbols->hashed-indices
(lambda(symbols) (let ((h (make-hash-table (length symbols)))
(count (let((c -1))(lambda()(set! c (+ c 1))c))))
(map (lambda(symbol)(hash-set! h symbol (count))) symbols)
h))))
(let-alias ((state-indices (vector-ref definition 0))
(method-indices (vector-ref definition 1))
(methods (vector-ref definition 2)))
; set the names of state variables into a hash table
(set! state-indices (symbols->hashed-indices properties))
; set the names of member functions into a hash table
(set! method-indices (symbols->hashed-indices (map car member-functions)))
(let* ((build-context (lambda(property)(list property (list 'vector-ref 'self (hash-ref state-indices property)))))
(context (map build-context properties))
;; method->lambda transforms member function definitions into appropreate lambdas. It builds
;; a lexical closure that aliases all properties of a given class as references to a vector "self".
;; for instance, member function 'a=b+n' in the following class definition:
;; (class '() '(a b) '((a=b+n (n) (set! a (+ b n)))))
;; will be transformed to:
;; (lambda (self n) (let-alias ((class (vector-ref self 0)) (a (vector-ref self 1)) (b (vector-ref self 2)))
;; (set! a (+ b n))))
(method->lambda (lambda(method-definition)
(let ((arglist (cons 'self (cadr method-definition))) ; list of method's arguments
(body (cddr method-definition))) ; body of the function
;; we `primitive-eval', because we don't want to have a function
;; definition (a list), but a function itself. This is guile-specific solution.
(primitive-eval `(lambda ,arglist (let-alias ,context ,@body)))))))
(set! methods (list->vector (map method->lambda member-functions)))))
definition))
(define-macro (in object method)
`(let* ((class (vector-ref ,object 0))
(name-to-method-map (vector-ref class 1))
(methods (vector-ref class 2))
(method-index (hash-ref name-to-method-map (car ,method)))
(member-function (vector-ref methods method-index)))
(apply member-function (cons ,object (cdr ,method)))))
(define-macro (get object property)
`(let* ((class (vector-ref ,object 0))
(name-to-value-map (vector-ref class 0))
(property-index (hash-ref name-to-value-map ,property)))
(vector-ref ,object property-index)))
(define (instance-of class . initial)
(let* ((l (length (hash-map->list cons (vector-ref class 0))))
(object (make-vector l)))
(vector-set! object 0 class)
(if (> (length initial) (- l 1))
(set! initial (list-head initial (- l 1))))
(let* ((count (let ((c 0)) (lambda () (set! c (+ c 1)) c))))
(map (lambda(value)(vector-set! object (count) value)) initial))
object))
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-13 22:42 SOS: Simple Object System Maciek Godek
@ 2008-09-14 10:22 ` Neil Jerram
2008-09-14 11:21 ` Greg Troxel
2008-09-15 6:48 ` Andy Wingo
2 siblings, 0 replies; 16+ messages in thread
From: Neil Jerram @ 2008-09-14 10:22 UTC (permalink / raw)
To: Maciek Godek; +Cc: guile-user
Hi Maciek,
Thanks for sharing this!
2008/9/14 Maciek Godek <pstrychuj@gmail.com>:
> Hi,
> Using some hints you gave me, I've implemented a really tiny
> object system -- and I would like to know your opinion ("why
> it's still better to use goops" :D)
You have already given a good summary of the pros and cons yourself, below.
> It has, as I can
> tell, a few advantages over goops -- mainly, storing objects
> as vectors allows for an efficient and convenient object treating
> from C level, so boatmen should be satisfied.
> Secondly, people accustomed to the object.method() notation
> (like myself) won't feel lost and the global namespace will be
> kept clean.
> The system certainly isn't as complex as goops and doesn't
> handle types (in general) so exquisitely. Also, it's unable to
> support multiple inheritance efficiently (single inheritance isn't
> supported as well, but this could be done quite easily if needed),
> but that's not my point.
I think the only big thing you missed was GOOPS's level of
customizability, which one you to create virtual slots, automatically
define slots for classes of a particular metaclass, and such like.
It's good to have another option than GOOPS. I imagine the major
reason someone might choose to use SOS instead of GOOPS would be the
C-level access. (And I hope we can one day work out something like
that for GOOPS!)
Regards,
Neil
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-13 22:42 SOS: Simple Object System Maciek Godek
2008-09-14 10:22 ` Neil Jerram
@ 2008-09-14 11:21 ` Greg Troxel
2008-09-15 6:48 ` Andy Wingo
2 siblings, 0 replies; 16+ messages in thread
From: Greg Troxel @ 2008-09-14 11:21 UTC (permalink / raw)
To: Maciek Godek; +Cc: guile-user
[-- Attachment #1: Type: text/plain, Size: 285 bytes --]
Your object system seems not to have a meta-object protocol.
There is an interesting and important book about this subject, and it's
worth reading if you haven't.
http://en.wikipedia.org/wiki/The_Art_of_the_Metaobject_Protocol
But, you said you are going for simple/less powerful.
[-- Attachment #2: Type: application/pgp-signature, Size: 193 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-13 22:42 SOS: Simple Object System Maciek Godek
2008-09-14 10:22 ` Neil Jerram
2008-09-14 11:21 ` Greg Troxel
@ 2008-09-15 6:48 ` Andy Wingo
2008-09-24 13:09 ` Maciek Godek
2 siblings, 1 reply; 16+ messages in thread
From: Andy Wingo @ 2008-09-15 6:48 UTC (permalink / raw)
To: Maciek Godek; +Cc: guile-user
Howdy,
On Sun 14 Sep 2008 00:42, "Maciek Godek" <pstrychuj@gmail.com> writes:
> Hi,
> Using some hints you gave me, I've implemented a really tiny
> object system
Neat!
> your opinion ("why it's still better to use goops" :D)
Use what you want :)
But:
> storing objects as vectors allows for an efficient and convenient
> object treating from C level, so boatmen should be satisfied.
GOOPS objects are internally implemented similar to vectors:
http://wingolog.org/archives/2008/04/11/allocate-memory-part-of-n
The only trick is that the mapping between slot names and indices in the
slot array is not part of GOOPS' specification -- it depends on many
things. But in normal cases, the first slot that you name in the class
definition is stored in slot 0, etc:
guile> (define-class <circle> () (radius #:init-keyword #:radius))
guile> (make <circle> #:radius 10)
$1 = #<<circle> b7f8be20>
guile> (struct-ref $2 0)
$2 = 10
Of course from scheme the way to take advantage of this is to use the
accessors, which compile down to this. But from C, probably your best
bet is to introspect which index a slot is stored in at runtime, then
cache that. From Guile-GNOME:
/* from goops.c */
static int
gtype_struct_offset (SCM class)
{
register SCM slots = SCM_SLOT (scm_class_of (class), scm_si_getters_n_setters);
for (; !scm_is_null (slots); slots = SCM_CDR (slots))
if (SCM_CAAR (slots) == scm_sym_gtype)
return scm_to_int (SCM_CDDR (SCM_CAR (slots)));
scm_c_gruntime_error ("%gtype-class-bind",
"`gtype' not allocated a slot in struct!",
SCM_LIST1 (class));
return -1;
}
This could certainly be improved, somehow, on an API level.
Andy
--
http://wingolog.org/
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-15 6:48 ` Andy Wingo
@ 2008-09-24 13:09 ` Maciek Godek
2008-09-24 16:14 ` Ludovic Courtès
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Maciek Godek @ 2008-09-24 13:09 UTC (permalink / raw)
To: guile-user
Thanks a lot for your all attention and clues.
If it comes to GOOPS, I think it would be best to
specify a well-defined C-level interface (for it would
go with the spirit of guile). This is the one thing.
The other is that in GOOPS a method is something
different than what is commonly known in OOP, because
a class doesn't know its methods (and furthermore,
methods can be created at any time of program execution,
not only during class definition). I'm not saying that
it's good or bad (but it's quite confusing when a few similar
but different notions share one name)
There is also another issue concerning the fact that
methods are available in global namespace -- the
performance of the interpreter is always penalized
by the type lookup (obviously, this doesn't have to
be the case if the code is compiled)
But the most important feature of OOP that is missed
in GOOPS (because of global namespace methods) is the lack
of the clean separation of interface and implementation
in the way it's done in java, C# and the like.
(At least that's what I think)
Thanks again
M.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 13:09 ` Maciek Godek
@ 2008-09-24 16:14 ` Ludovic Courtès
2008-09-24 18:00 ` Clinton Ebadi
` (2 subsequent siblings)
3 siblings, 0 replies; 16+ messages in thread
From: Ludovic Courtès @ 2008-09-24 16:14 UTC (permalink / raw)
To: guile-user
Hi,
"Maciek Godek" <pstrychuj@gmail.com> writes:
> The other is that in GOOPS a method is something
> different than what is commonly known in OOP,
The design of GOOPS is based on that of CLOS, so GOOPS is surely
familiar to anyone familiar with the CLOS flavor of OOP. :-)
See http://en.wikipedia.org/wiki/CLOS for details.
> because a class doesn't know its methods
It does:
guile> (length (class-methods <object>))
$4 = 54
But unlike in C++, Java, etc., a method is not bound to the type of its
first argument: method dispatching is done on all arguments and on the
number of arguments as well.
> There is also another issue concerning the fact that
> methods are available in global namespace
That's not quite true: the namespace, be it for GOOPS methods
("generics" actually) or "regular" Scheme objects, is managed by the
module system. Thus, it is possible to hide generics (and,
consequently, the methods they contain) from users, simply by not
exporting them from the module where they are defined.
> But the most important feature of OOP that is missed
> in GOOPS (because of global namespace methods) is the lack
> of the clean separation of interface and implementation
> in the way it's done in java, C# and the like.
See above.
Does that lessen your concerns?
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 13:09 ` Maciek Godek
2008-09-24 16:14 ` Ludovic Courtès
@ 2008-09-24 18:00 ` Clinton Ebadi
2008-09-24 21:04 ` Maciek Godek
2008-09-24 22:25 ` Jon Wilson
2008-09-24 22:45 ` Jon Wilson
3 siblings, 1 reply; 16+ messages in thread
From: Clinton Ebadi @ 2008-09-24 18:00 UTC (permalink / raw)
To: Maciek Godek; +Cc: guile-user
"Maciek Godek" <pstrychuj@gmail.com> writes:
> The other is that in GOOPS a method is something
> different than what is commonly known in OOP, because
> a class doesn't know its methods (and furthermore,
> methods can be created at any time of program execution,
> not only during class definition). I'm not saying that
> it's good or bad (but it's quite confusing when a few similar
> but different notions share one name)
Interestingly enough, CLOS predates C++ and Java ;-)
Separating classes and generic functions makes the language more
powerful--now two orthogonal concepts may be combined in arbitrary
ways. The classic example of having generic functions are multimethods
wherein you can dispatch on the type of every argument of the
generic. A simple example of this is the (simplified) present method
from CLIM:
(define-generic present (instance view))
Which then allows you to do nifty things like:
(define-method (present (instance <image>) (view <text-view>))
(format #t "[~A]" (alt-text-of instance)))
(define-method (present (instance <image>) (view <graphical-view>))
(display the image somehow))
etc.
Doing this with a single-dispatch system is much less aesthetically
pleasing. Note also that multimethods are only one of many advantages
to having generic functions--they also enable method combinations and
a few other things. Stylistically, they make the OO system integrate
cleanly with the rest of the language rather than having its own
specialized syntax for method invocation.
> There is also another issue concerning the fact that
> methods are available in global namespace -- the
> performance of the interpreter is always penalized
> by the type lookup (obviously, this doesn't have to
> be the case if the code is compiled)
Type lookup in GOOPS should be very fast--there are a few fairly
simple implementation techniques that more or less eliminate the
overhead of method dispatch (or at least turn it into a few very low
overhead table lookups). /The Art of the Metaobject Protocol/ is a
good inexpensive book that gives a decent overview of how to implement
a fast CLOS.
> But the most important feature of OOP that is missed
> in GOOPS (because of global namespace methods) is the lack
> of the clean separation of interface and implementation
> in the way it's done in java, C# and the like.
Actually, CLOS/GOOPS are perhaps the cleanest way to separate
interface from implementation. You define a protocol as a set of
generic functions and define methods upon them -- with no concern for
the actual classes used with the protocol.
In this way you can do implementation sharing via class inheritance or
something akin to Java interfaces (if I understand them properly; I
refuse to use such a language) and implement the protocol for
arbitrary classes without having to arbitrarily force them to inherit
from unrelated classes.
--
Jessie: i stuck the phone antenna up the dogs nose and he ignored me
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 18:00 ` Clinton Ebadi
@ 2008-09-24 21:04 ` Maciek Godek
2008-09-24 22:14 ` David Séverin
` (2 more replies)
0 siblings, 3 replies; 16+ messages in thread
From: Maciek Godek @ 2008-09-24 21:04 UTC (permalink / raw)
To: Clinton Ebadi; +Cc: guile-user
2008/9/24 Clinton Ebadi <clinton@unknownlamer.org>:
>
>> The other is that in GOOPS a method is something
>> different than what is commonly known in OOP, because
>> a class doesn't know its methods (and furthermore,
>> methods can be created at any time of program execution,
>> not only during class definition). I'm not saying that
>> it's good or bad (but it's quite confusing when a few similar
>> but different notions share one name)
>
> Interestingly enough, CLOS predates C++ and Java ;-)
No wonder -- lambda calculus is a formal system to express concepts
and you can express practically anything in it. C was invented to program
von Neumann machines, and both C++ and Java are derivatives of C.
Note however that both these languages were invented to address
the particular needs of OOP and as such are well suited for that purpose
(or at least that's what the most people think. CLOS is rarely used
compared to C++ and Java -- can you explain why?)
> Separating classes and generic functions makes the language more
> powerful--now two orthogonal concepts may be combined in arbitrary
> ways. The classic example of having generic functions are multimethods
> wherein you can dispatch on the type of every argument of the
> generic. A simple example of this is the (simplified) present method
> from CLIM:
>
> (define-generic present (instance view))
>
> Which then allows you to do nifty things like:
>
> (define-method (present (instance <image>) (view <text-view>))
> (format #t "[~A]" (alt-text-of instance)))
> (define-method (present (instance <image>) (view <graphical-view>))
> (display the image somehow))
>
> etc.
And what about
(define-method (present (zone <timezone>) (part-of-year <season>))
(define-method (present (santa <donor>) (child <acceptor>)))
?
I mean of course these examples are artificial, but that's not their point :)
(I won't expose it now to keep the reader in suspense, but stay tuned
as it will certainly appear in the sequel)
> Doing this with a single-dispatch system is much less aesthetically
> pleasing. Note also that multimethods are only one of many advantages
> to having generic functions--they also enable method combinations and
> a few other things. Stylistically, they make the OO system integrate
> cleanly with the rest of the language rather than having its own
> specialized syntax for method invocation.
I see your point, but I can't agree with it -- multimethods certainly
cause ambiguity and make the code harder to read (you can't tell
which method will be called unless you know the types of all its
arguments).
I've got a feeling that CLOS is an attempt to mimic synonymy that
is present (yes, present!) in natural languages. Yet it causes a lot
of confusion!
Note that in C++like syntax, you always precede the name of the
method with the object's name, and therefore you always specify
the context for your expression. (the object is the context)
This resembles the let form known from lisp, that is also used
to build context. (No expression in the world is context-free, but
the multimethods of CLOS seem to deny this simple truth)
>> There is also another issue concerning the fact that
>> methods are available in global namespace -- the
>> performance of the interpreter is always penalized
>> by the type lookup (obviously, this doesn't have to
>> be the case if the code is compiled)
>
> Type lookup in GOOPS should be very fast--there are a few fairly
> simple implementation techniques that more or less eliminate the
> overhead of method dispatch (or at least turn it into a few very low
> overhead table lookups). /The Art of the Metaobject Protocol/ is a
> good inexpensive book that gives a decent overview of how to implement
> a fast CLOS.
I'm not arguing about this -- especially that there's great conceptual
gain that results with this sort of elasticity. (Performance is the last
thing I care about right now. But I hope you're right :>)
>> But the most important feature of OOP that is missed
>> in GOOPS (because of global namespace methods) is the lack
>> of the clean separation of interface and implementation
>> in the way it's done in java, C# and the like.
>
> Actually, CLOS/GOOPS are perhaps the cleanest way to separate
> interface from implementation. You define a protocol as a set of
> generic functions and define methods upon them -- with no concern for
> the actual classes used with the protocol.
The disadvantage is that (unlike in Java, C# etc.) the "interface" isn't an
explicit object. I agree that this can be overcome with a proper discipline
imposed on source code, but the documentation of GOOPS makes me
think that no such discipline has yet been developed.
> In this way you can do implementation sharing via class inheritance or
> something akin to Java interfaces (if I understand them properly; I
> refuse to use such a language) and implement the protocol for
> arbitrary classes without having to arbitrarily force them to inherit
> from unrelated classes.
I don't quite get what you mean (I'd need an example).
BTW Java seems to gain popularity instead of loosing it.
(And I think that stupidity of mankind and human ignorance
are not the only reasons)
I admit that my arguments are somewhat abstract -- I haven't written
anything using GOOPS or CLOS, only read some documentation
(and although my mind was initially tabula rasa, but as I was getting
deeper into that forest, I kept having more and more doubts. Maybe
it's because I knew C++ before GOOPS?)
Best regards
M.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 21:04 ` Maciek Godek
@ 2008-09-24 22:14 ` David Séverin
2008-09-24 22:38 ` Clinton Ebadi
2008-09-25 13:58 ` David Séverin
2 siblings, 0 replies; 16+ messages in thread
From: David Séverin @ 2008-09-24 22:14 UTC (permalink / raw)
To: Maciek Godek; +Cc: Clinton Ebadi, guile-user
Le Wed, 24 Sep 2008 23:04:51 +0200,
"Maciek Godek" <pstrychuj@gmail.com> a écrit :
> Note however that both these languages were invented to address
> the particular needs of OOP and as such are well suited for that purpose
> (or at least that's what the most people think. CLOS is rarely used
> compared to C++ and Java -- can you explain why?)
because most people are ... and 'nobody will ever be killed to propose the
use of C++ and java ...' people like to conform to others, like your question
shows, are you under some kind of industrial pressure ? : who says,
in a scientific community, that a system is 'good' because 'most people uses
it' ?
I personally think that, from a grammar and semantic point of view, CLOS and
GOOP are superior, by far, to other OOP systems, as the few responses on this
list pointed out [but there are a lot more to say and articles to read about
this matter]
IMHO
david
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 13:09 ` Maciek Godek
2008-09-24 16:14 ` Ludovic Courtès
2008-09-24 18:00 ` Clinton Ebadi
@ 2008-09-24 22:25 ` Jon Wilson
2008-09-24 22:45 ` Jon Wilson
3 siblings, 0 replies; 16+ messages in thread
From: Jon Wilson @ 2008-09-24 22:25 UTC (permalink / raw)
To: guile-user
Hi,
You might be interested to read Jonathan Rees' take on OO. It certainly
broadens the mind regarding the various implementations and terminology
of OO, but is still quite brief. I certainly don't think the Java/C++
model is the last word in OO (but I didn't think that before reeding
Rees, either)
http://www.paulgraham.com/reesoo.html
Regards,
Jon
Maciek Godek wrote:
> Thanks a lot for your all attention and clues.
>
> If it comes to GOOPS, I think it would be best to
> specify a well-defined C-level interface (for it would
> go with the spirit of guile). This is the one thing.
>
> The other is that in GOOPS a method is something
> different than what is commonly known in OOP, because
> a class doesn't know its methods (and furthermore,
> methods can be created at any time of program execution,
> not only during class definition). I'm not saying that
> it's good or bad (but it's quite confusing when a few similar
> but different notions share one name)
>
> There is also another issue concerning the fact that
> methods are available in global namespace -- the
> performance of the interpreter is always penalized
> by the type lookup (obviously, this doesn't have to
> be the case if the code is compiled)
>
> But the most important feature of OOP that is missed
> in GOOPS (because of global namespace methods) is the lack
> of the clean separation of interface and implementation
> in the way it's done in java, C# and the like.
>
> (At least that's what I think)
>
> Thanks again
> M.
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 21:04 ` Maciek Godek
2008-09-24 22:14 ` David Séverin
@ 2008-09-24 22:38 ` Clinton Ebadi
2008-09-25 23:03 ` Linas Vepstas
2008-09-26 14:20 ` Maciek Godek
2008-09-25 13:58 ` David Séverin
2 siblings, 2 replies; 16+ messages in thread
From: Clinton Ebadi @ 2008-09-24 22:38 UTC (permalink / raw)
To: Maciek Godek; +Cc: guile-user
"Maciek Godek" <pstrychuj@gmail.com> writes:
> 2008/9/24 Clinton Ebadi <clinton@unknownlamer.org>:
>> Interestingly enough, CLOS predates C++ and Java ;-)
>
> No wonder -- lambda calculus is a formal system to express concepts
> and you can express practically anything in it. C was invented to program
> von Neumann machines, and both C++ and Java are derivatives of C.
C predates Common Lisp and any Lisp object system :-) Note that the OO
features of C++ are *not* derived from C but rather from Simula.
> Note however that both these languages were invented to address
> the particular needs of OOP and as such are well suited for that purpose
> (or at least that's what the most people think. CLOS is rarely used
> compared to C++ and Java -- can you explain why?)
Popularity as a measure of quality is a fallacious argument (one that
many people often use). Java's widespread adoption can be explained by
Sun heavily promoting it with their deep pockets, and C++ as being an
extension of C and coming from Bell Labs.
Remember that Lisp was not usable on workstations and personal
computers until the 90s and the Lisp machines of hte 70s and 80s had a
drastically different OS compared to UNIX. As such it has only been in
the last ten years that Common Lisp/Scheme have become usable for
tasks that C++/Java/etc are used for. There are *very* large
systems--ITA's airline ticket finding system is the canonical example
of a thriving Lisp system.
Lisp was traditionally used for things like expert systems, planners,
CAD, etc. The first two are still not done well by either C++ or Java.
>> Separating classes and generic functions makes the language more
>> powerful--now two orthogonal concepts may be combined in arbitrary
>> ways. The classic example of having generic functions are multimethods
>> wherein you can dispatch on the type of every argument of the
>> generic. A simple example of this is the (simplified) present method
>> from CLIM:
>>
>> (define-generic present (instance view))
>>
>> Which then allows you to do nifty things like:
>>
>> (define-method (present (instance <image>) (view <text-view>))
>> (format #t "[~A]" (alt-text-of instance)))
>> (define-method (present (instance <image>) (view <graphical-view>))
>> (display the image somehow))
>>
>> etc.
>
> And what about
> (define-method (present (zone <timezone>) (part-of-year <season>))
> (define-method (present (santa <donor>) (child <acceptor>)))
> ?
What would the timezone method do? Perhaps you meant:
(define-generic present-time-zone? (part-of-year))
As it doesn't make much sense to supply the answer to the predicate as
the first argument (unless you meant something).
The other example is solvable via namespaces: You can have a clim and
a gift module, and then reference them as
clim:present/gift:present. The guile syntax for doing this is not so
nice now--((@ (clim) present) ...), but it doesn't have to be (and you
can always use a #:renamer with use-modules).
>> Doing this with a single-dispatch system is much less aesthetically
>> pleasing. Note also that multimethods are only one of many advantages
>> to having generic functions--they also enable method combinations and
>> a few other things. Stylistically, they make the OO system integrate
>> cleanly with the rest of the language rather than having its own
>> specialized syntax for method invocation.
>
> I see your point, but I can't agree with it -- multimethods certainly
> cause ambiguity and make the code harder to read (you can't tell
> which method will be called unless you know the types of all its
> arguments).
The point of OO methods is that you need not concern yourself with
which method is called: every method on a generic has the same
behavior. If it does not then it violates the protocol, and you can
certainly do this in a single dispatch system as well.
>>> But the most important feature of OOP that is missed
>>> in GOOPS (because of global namespace methods) is the lack
>>> of the clean separation of interface and implementation
>>> in the way it's done in java, C# and the like.
>>
>> Actually, CLOS/GOOPS are perhaps the cleanest way to separate
>> interface from implementation. You define a protocol as a set of
>> generic functions and define methods upon them -- with no concern for
>> the actual classes used with the protocol.
>
> The disadvantage is that (unlike in Java, C# etc.) the "interface" isn't an
> explicit object. I agree that this can be overcome with a proper discipline
> imposed on source code, but the documentation of GOOPS makes me
> think that no such discipline has yet been developed.
This is a bit of an open problem even if the other languages. You can
specify a collective interface in Java (or a mixin class in C++), but
you still cannot specify the required behavior of the protocol except
in an ad hoc fashion. This is a problem that may never be solved (and
is perhaps a sign that OO is not so great).
You could, of course, add packaged protocol interfaces to GOOPS using
the MOP and a few macros:
(define-protocol-interface NAME
(define-generic ...) ...)
(define-protocol-implementation NAME CLASS
(define-method ...) ...)
Along with some metaclass magic to ensure that the entire protocol is
indeed defined withing the `define-protocol-implementation' block. The
benefits of this, however, are dubious as you still cannot guarantee
anything about the behavior of the implementation in a programmatic
way.
>> In this way you can do implementation sharing via class inheritance or
>> something akin to Java interfaces (if I understand them properly; I
>> refuse to use such a language) and implement the protocol for
>> arbitrary classes without having to arbitrarily force them to inherit
>> from unrelated classes.
>
> I don't quite get what you mean (I'd need an example).
> BTW Java seems to gain popularity instead of loosing it.
> (And I think that stupidity of mankind and human ignorance
> are not the only reasons)
Say that you have a system wherein objects can move (obviously an
artificially simple example):
(define-generic move (thing point))
Now say that you have machines, animals, etc. In a Smalltalkesque OO
system you would need to do something akin to:
(define-class movable-object ()
(#:methods (move (thing point) ...)
...) ...)
(define-class machine (... movable-object ...)
...)
(define-class animal (... movable-object ...)
...)
Taxonimically animals and machines are *not* related by their ability
to move, but they can both move. Thus you run into a problem with your
type discipline as a result. In a language with orthogonal generics
and classes you can simply implement the `move' method for anything
that can move without polluting your class heirarchy. This is
especially important if you are doing taxonomic reasoning of any sort.
Now, you may perhaps raise the objection: "But how would I tell which
classes specialized `move'?" In CLOS at least (I am not quite familiar
enough with the GOOPS internals) you can query the generic function
object for a list of classes that specialize any of its arguments
since the MOP guarantees that this information will be maintained.
I hope this clears things up a bit.
--
"Karen loved animals. Unfortunately the cheetahs betrayed her trust,"
Libot said.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 13:09 ` Maciek Godek
` (2 preceding siblings ...)
2008-09-24 22:25 ` Jon Wilson
@ 2008-09-24 22:45 ` Jon Wilson
3 siblings, 0 replies; 16+ messages in thread
From: Jon Wilson @ 2008-09-24 22:45 UTC (permalink / raw)
To: guile-user
Hi again,
For an example of an OO system that is even more wildly different, but
still quite interestingly useful, look up the Prometheus object system
for Scheme. I don't know if there is a version that would happily run
on Guile, but there are versions for at least Scheme48 and PLT, IIRC.
It is prototype based, and really quite fascinating.
Regards,
Jon
Maciek Godek wrote:
> Thanks a lot for your all attention and clues.
>
> If it comes to GOOPS, I think it would be best to
> specify a well-defined C-level interface (for it would
> go with the spirit of guile). This is the one thing.
>
> The other is that in GOOPS a method is something
> different than what is commonly known in OOP, because
> a class doesn't know its methods (and furthermore,
> methods can be created at any time of program execution,
> not only during class definition). I'm not saying that
> it's good or bad (but it's quite confusing when a few similar
> but different notions share one name)
>
> There is also another issue concerning the fact that
> methods are available in global namespace -- the
> performance of the interpreter is always penalized
> by the type lookup (obviously, this doesn't have to
> be the case if the code is compiled)
>
> But the most important feature of OOP that is missed
> in GOOPS (because of global namespace methods) is the lack
> of the clean separation of interface and implementation
> in the way it's done in java, C# and the like.
>
> (At least that's what I think)
>
> Thanks again
> M.
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 21:04 ` Maciek Godek
2008-09-24 22:14 ` David Séverin
2008-09-24 22:38 ` Clinton Ebadi
@ 2008-09-25 13:58 ` David Séverin
2008-09-25 17:17 ` Maciek Godek
2 siblings, 1 reply; 16+ messages in thread
From: David Séverin @ 2008-09-25 13:58 UTC (permalink / raw)
To: Maciek Godek; +Cc: Clinton Ebadi, guile-user
Le Wed, 24 Sep 2008 23:04:51 +0200,
"Maciek Godek" <pstrychuj@gmail.com> a écrit :
> ...
> Note however that both these languages were invented to address
> the particular needs of OOP and as such are well suited for that purpose
> (or at least that's what the most people think. CLOS is rarely used
> compared to C++ and Java -- can you explain why?)
also because people in command sometimes have fear to take their
responsability: here in Rio de Janeiro, 2 years ago, at PUC University, the
scheme course has been canceled [for ever] because ... students didn't
want to be teached scheme anymore ?!?
so, 18 years old kids now decide what they should be teached at
university level ... and guess what they 'quasy all' want ...
this leads to a very 'dangerous' standardization of computer technique skills
available at industrial level, lack of creativity [creativity is the rule, not
the execption, but people need to be motivated and teached to trust themselves
they are creative, and in our field, computer science, scheme is [also but can
not be reduced to this only off course] an excellent creativity trigger.
in my original town in Belgium, Ouindoze offered [24 years ago] that the
operating system course at university be based on their system [fortunatly this
was refused] then they would offer the material and licenses ... so that
'people would already be ready to work when quitting university' and would not
need training when they start working ... blabla cost effective for the
society ... blabla blablabla
it's like wine: big companies purchased small ones, private small wine
producer, and converted the taste of the small producer to the standard taste,
result: you here in the population, that the wine is good if it taste like the
others [and they now and since may years already change the taste chemically]
david
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-25 13:58 ` David Séverin
@ 2008-09-25 17:17 ` Maciek Godek
0 siblings, 0 replies; 16+ messages in thread
From: Maciek Godek @ 2008-09-25 17:17 UTC (permalink / raw)
To: David Séverin; +Cc: guile-user
2008/9/25 David Séverin <david@altosw.be>:
>> (or at least that's what the most people think. CLOS is rarely used
>> compared to C++ and Java -- can you explain why?)
>
> also because people in command sometimes have fear to take their
> responsability: here in Rio de Janeiro, 2 years ago, at PUC University, the
> scheme course has been canceled [for ever] because ... students didn't
> want to be teached scheme anymore ?!?
>
> so, 18 years old kids now decide what they should be teached at
> university level ... and guess what they 'quasy all' want ...
>
> this leads to a very 'dangerous' standardization of computer technique skills
> available at industrial level, lack of creativity [creativity is the rule, not
> the execption, but people need to be motivated and teached to trust themselves
> they are creative, and in our field, computer science, scheme is [also but can
> not be reduced to this only off course] an excellent creativity trigger.
The difference between, say, C and lisp is that C is intended for programming
computers, and lisp is a notation for programming in general (you don't have
any implicit memory model -- conversely, lambda calculus is probably the most
universal way to express algorithm). This is visible when you compare "Structure
and Interpretation.." or "How to design programs" to "The C
Programming Language"
-- it seems that schemers are aware that programs are present not only inside of
computers, but wherever some sort of control is.
On the other hand, people prefer braces and infix notation to express computer
programs -- probably because it's the notation they got used to on their math
classes.
> in my original town in Belgium, Ouindoze offered [24 years ago] that the
> operating system course at university be based on their system [fortunatly this
> was refused] then they would offer the material and licenses ... so that
> 'people would already be ready to work when quitting university' and would not
> need training when they start working ... blabla cost effective for the
> society ... blabla blablabla
:] Yes, that's a clever way to utilize public money.
(And that's funny about software that it really costs nothing to give you this
license). I'd love to complain on the actions of microsoft and praise
greatness of
freebsd, but it's not the proper place :(
> it's like wine: big companies purchased small ones, private small wine
> producer, and converted the taste of the small producer to the standard taste,
> result: you here in the population, that the wine is good if it taste like the
> others [and they now and since may years already change the taste chemically]
standardized men drinking only corporate wine and programming standardized
applications in java, having their lives perfectly predicted before
they were even born.
let's not drink for them :)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 22:38 ` Clinton Ebadi
@ 2008-09-25 23:03 ` Linas Vepstas
2008-09-26 14:20 ` Maciek Godek
1 sibling, 0 replies; 16+ messages in thread
From: Linas Vepstas @ 2008-09-25 23:03 UTC (permalink / raw)
To: Clinton Ebadi; +Cc: guile-user
2008/9/24 Clinton Ebadi <clinton@unknownlamer.org>:
> This is a bit of an open problem even if the other languages. You can
> specify a collective interface in Java (or a mixin class in C++), but
> you still cannot specify the required behavior of the protocol except
> in an ad hoc fashion. This is a problem that may never be solved (and
> is perhaps a sign that OO is not so great).
There are certainly many other problems with the OO
paradigm. I have coded in Java, and one of its greatest
failings is that it *forces* you to associate every function
to some object -- It forces you into designing interfaces
even when you are only interested in implementation.
This is not a problem for some 90% of all the functions
one typically ahs to create in some project.
However, during architecture, design, development,
there's always a few "tricky" areas, where its simply
not clear where to implement some function -- there
may be several choices, all not very good, etc.
However, Java *forces* you to stick such functions
into a class, some class, any class, even if they don't
really belong there. And once you do that, the next
thing you know, they are so tightly tied in, that it
becomes impossible (well, overwhelmingly difficult)
to refactor the code. This is a variant of the so-called
"quick-setting concrete" effect seen in C++.
In real life, design needs to be more flexible, more
moldable, and code needs to be easy to change.
Many object systems seem to make a point of
inhibiting design change, by forcing you design
(and use) an interface before you are intellectually
ready to design the interface.
--linas
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: SOS: Simple Object System
2008-09-24 22:38 ` Clinton Ebadi
2008-09-25 23:03 ` Linas Vepstas
@ 2008-09-26 14:20 ` Maciek Godek
1 sibling, 0 replies; 16+ messages in thread
From: Maciek Godek @ 2008-09-26 14:20 UTC (permalink / raw)
To: guile-user
2008/9/25 Clinton Ebadi <clinton@unknownlamer.org>:
>> No wonder -- lambda calculus is a formal system to express concepts
>> and you can express practically anything in it. C was invented to program
>> von Neumann machines, and both C++ and Java are derivatives of C.
>
> C predates Common Lisp and any Lisp object system :-) Note that the OO
> features of C++ are *not* derived from C but rather from Simula.
I didn't know that. I meant that they adopt machine model from C.
(that's is quite strange a combination, making C++ very incoherent)
>> Note however that both these languages were invented to address
>> the particular needs of OOP and as such are well suited for that purpose
>> (or at least that's what the most people think. CLOS is rarely used
>> compared to C++ and Java -- can you explain why?)
>
> Popularity as a measure of quality is a fallacious argument (one that
> many people often use). Java's widespread adoption can be explained by
> Sun heavily promoting it with their deep pockets, and C++ as being an
> extension of C and coming from Bell Labs.
C++ is the deepest septic tank I've ever fallen into :)
Fortunately, these approaches weren't successful. OK -- java is
actually used to code games on mobile phones, but the most of
the projects on sourceforge are programmed in C (thus avoiding
all the problems generated by the handicapped object system
of C++)
> Remember that Lisp was not usable on workstations and personal
> computers until the 90s and the Lisp machines of hte 70s and 80s had a
> drastically different OS compared to UNIX. As such it has only been in
> the last ten years that Common Lisp/Scheme have become usable for
> tasks that C++/Java/etc are used for. There are *very* large
> systems--ITA's airline ticket finding system is the canonical example
> of a thriving Lisp system.
That's a good point -- in C there arel programs, and in Lisp -- systems
(or organisms, as the authors of SICP would say)
Now the situation of Lisp seems much clearer to me. Unfortunately
the coding style in lisp is still strongly diversified, there are too many
OO systems and practically no standards (CLOS is probably nearly
a standard, GOOPS is available in guile ony). C has become very
common; for Java, there's one official SDK that is a good starting
point for everyone who's interested. If you're willing to program in
Scheme, there are many choices (I guess PLT is good to start
with, but I'm afraid it also has its limitations)
There are still some features guile is lacking: firstly, there is no
simple way to call C code from the level of guile -- you have to write
frequently trivial wrappers to make it work, while having a C parser
and a single wrapper on dlfcn library would suffice (you could then
acquire symbols from header files and link them with appropriate
DL objects -- this way, guile would obtain the frequently advertised
feature of C++ -- the ability to call C functions directly)
The second thing is the performance issue: you can't compile
directly to machine code (on the fly), and the practice of byte-compiling
(with guile-vm) is still avaliable for the chosen ones, but I'm really glad to
see Andy's efforts to set it all up. (Thank you, Andy!)
Guile, however, offers the level of openness and extensibility
that allows it to be constantly improved (and that lessens my
concerns)
> Lisp was traditionally used for things like expert systems, planners,
> CAD, etc. The first two are still not done well by either C++ or Java.
Java is quite good for web applets and the aforementioned moblie
phones, and C++ is really great in causing trouble and annoying people.
Scheme was never meant for that purpose (but I think that's doable)
>>> (define-generic present (instance view))
>>>
>>> Which then allows you to do nifty things like:
>>>
>>> (define-method (present (instance <image>) (view <text-view>))
>>> (format #t "[~A]" (alt-text-of instance)))
>>> (define-method (present (instance <image>) (view <graphical-view>))
>>> (display the image somehow))
>>>
>>> etc.
>>
>> And what about
>> (define-method (present (zone <timezone>) (part-of-year <season>))
>> (define-method (present (santa <donor>) (child <acceptor>)))
>> ?
>
> What would the timezone method do? Perhaps you meant:
>
> (define-generic present-time-zone? (part-of-year))
>
> As it doesn't make much sense to supply the answer to the predicate as
> the first argument (unless you meant something).
Well, never mind that (I actually meant a method that
returns present time in given timezone, but I wrote it
slightly thoughtlessly, hence the part-of-year argument)
> The other example is solvable via namespaces: You can have a clim and
> a gift module, and then reference them as
> clim:present/gift:present. The guile syntax for doing this is not so
> nice now--((@ (clim) present) ...), but it doesn't have to be (and you
> can always use a #:renamer with use-modules).
I'm getting to like the whole idea of modules more and more :)
>>> Doing this with a single-dispatch system is much less aesthetically
>>> pleasing. Note also that multimethods are only one of many advantages
>>> to having generic functions--they also enable method combinations and
>>> a few other things. Stylistically, they make the OO system integrate
>>> cleanly with the rest of the language rather than having its own
>>> specialized syntax for method invocation.
>>
>> I see your point, but I can't agree with it -- multimethods certainly
>> cause ambiguity and make the code harder to read (you can't tell
>> which method will be called unless you know the types of all its
>> arguments).
>
> The point of OO methods is that you need not concern yourself with
> which method is called: every method on a generic has the same
> behavior. If it does not then it violates the protocol, and you can
> certainly do this in a single dispatch system as well.
As for me, the advantage of classes is that they gather operations
that you can perform on an object in one place (object's class
definition). This way, the documentation of source code is really
well structured.
(Besides it is a matter of thinking about it: I agree that when you
rotate a box in the air, you rotate box, not box.rotate(), but when
you push play button on VCR, then you VCR.play())
- Pokaż cytowany tekst -
>>>> But the most important feature of OOP that is missed
>>>> in GOOPS (because of global namespace methods) is the lack
>>>> of the clean separation of interface and implementation
>>>> in the way it's done in java, C# and the like.
>>>
>>> Actually, CLOS/GOOPS are perhaps the cleanest way to separate
>>> interface from implementation. You define a protocol as a set of
>>> generic functions and define methods upon them -- with no concern for
>>> the actual classes used with the protocol.
>>
>> The disadvantage is that (unlike in Java, C# etc.) the "interface" isn't an
>> explicit object. I agree that this can be overcome with a proper discipline
>> imposed on source code, but the documentation of GOOPS makes me
>> think that no such discipline has yet been developed.
>
> This is a bit of an open problem even if the other languages. You can
> specify a collective interface in Java (or a mixin class in C++), but
> you still cannot specify the required behavior of the protocol except
> in an ad hoc fashion. This is a problem that may never be solved (and
> is perhaps a sign that OO is not so great).
>
> You could, of course, add packaged protocol interfaces to GOOPS using
> the MOP and a few macros:
>
> (define-protocol-interface NAME
> (define-generic ...) ...)
>
> (define-protocol-implementation NAME CLASS
> (define-method ...) ...)
>
> Along with some metaclass magic to ensure that the entire protocol is
> indeed defined withing the `define-protocol-implementation' block. The
> benefits of this, however, are dubious as you still cannot guarantee
> anything about the behavior of the implementation in a programmatic
> way.
Yeah, guess you're right. Maybe this multiplicity of choices and lack
of common solutions on many fields makes scheme programming
harder.
>>> In this way you can do implementation sharing via class inheritance or
>>> something akin to Java interfaces (if I understand them properly; I
>>> refuse to use such a language) and implement the protocol for
>>> arbitrary classes without having to arbitrarily force them to inherit
>>> from unrelated classes.
>>
>> I don't quite get what you mean (I'd need an example).
>> BTW Java seems to gain popularity instead of loosing it.
>> (And I think that stupidity of mankind and human ignorance
>> are not the only reasons)
>
> Say that you have a system wherein objects can move (obviously an
> artificially simple example):
Oh, at last, an example! :)
> (define-generic move (thing point))
>
> Now say that you have machines, animals, etc. In a Smalltalkesque OO
> system you would need to do something akin to:
>
> (define-class movable-object ()
> (#:methods (move (thing point) ...)
> ...) ...)
> (define-class machine (... movable-object ...)
> ...)
> (define-class animal (... movable-object ...)
> ...)
>
> Taxonimically animals and machines are *not* related by their ability
> to move, but they can both move. Thus you run into a problem with your
> type discipline as a result. In a language with orthogonal generics
> and classes you can simply implement the `move' method for anything
> that can move without polluting your class heirarchy. This is
> especially important if you are doing taxonomic reasoning of any sort.
You've convinced me -- MOP certainly gives more flexibility.
It doesn't structure the way you program, and doesn't take you by the
hand (which is sometimes desirable, especially when you begin).
> Now, you may perhaps raise the objection: "But how would I tell which
> classes specialized `move'?" In CLOS at least (I am not quite familiar
> enough with the GOOPS internals) you can query the generic function
> object for a list of classes that specialize any of its arguments
> since the MOP guarantees that this information will be maintained.
>
> I hope this clears things up a bit.
It certainly does. Thanks.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-09-26 14:20 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-13 22:42 SOS: Simple Object System Maciek Godek
2008-09-14 10:22 ` Neil Jerram
2008-09-14 11:21 ` Greg Troxel
2008-09-15 6:48 ` Andy Wingo
2008-09-24 13:09 ` Maciek Godek
2008-09-24 16:14 ` Ludovic Courtès
2008-09-24 18:00 ` Clinton Ebadi
2008-09-24 21:04 ` Maciek Godek
2008-09-24 22:14 ` David Séverin
2008-09-24 22:38 ` Clinton Ebadi
2008-09-25 23:03 ` Linas Vepstas
2008-09-26 14:20 ` Maciek Godek
2008-09-25 13:58 ` David Séverin
2008-09-25 17:17 ` Maciek Godek
2008-09-24 22:25 ` Jon Wilson
2008-09-24 22:45 ` Jon Wilson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).