This is the mail archive of the guile@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: tiny-clos-questions


Ken Anderson <kanderso@bbn.com> writes:

> At 11:39 AM 11/7/98 +0100, Klaus Schilling wrote:
> >Craig Brozefsky writes:
> > > MAILER-DAEMON@cx606007-a.irvn1.occa.home.com writes:

ouch, that's an ugly header I've got!

To start things off, let me say that I'm not an OO booster and I
really think it's overplayed.  Still, when I do think that an OO model
best fits what I'm working with, I want something capable of
satisfying my ontological perversity.

> >Tinyclos is too clumsy for tasks that don't require a meta object protocol.
> >Thus I use a lightweight object system like BOS. Boekemeier wouldn't call it
> >an object system, though.
> 
> TinyCLOS may seem clumsy, but the meta object protocol is a good thing.
> Tiny is a sketch, a very elegant sketch, of how to do a MOP based object
> system.

MOPs are so very nice, and the simplicity of tiny-clos makes it very
easy for someone to understand how a MOP works, and how to use it to
their advantage.  That is something that should not be underestimated.
A MOP in combination with the awesome meta-programming power of scheme
lets us throw together nearly any kind of OO system we want, and it
lets programmers choose the idioms they like.  I like generic
functions myself, being polymorphically perverse if you will, but
that's a personal taste.

> It is too tiny in many ways, but it is straight forward to make it useful.
> For example, every object in tiny is opague, in the Scheme tradition.  This
> makes debugging extremely painful.  A solution is to give metaobjects names,
> like most object systems do.  Also, method lookup is done from scratch for
> each generic function invocation.  But, method lookup can be memoized.

Well, it's tiny, but the difference between it, and what I could see
becoming the guile OO system of choice does not require much more than
a few bug fixes, and layering some user freindly macros on top of it.
The method combination and the ability of it to deal with keyword and
optional arguments is the only problem I see, and that's solveable in
a night or so once we figure out how we want the method specialization
to work with keywords and optionals.

This is pretty much what I just did in the last couple hours (as well
as make a most kick ass curry).  After seeing this message from you I
figured I should just code up some of the ideas I had.  I now have
CLOS like macros for defining classes and methods, and have extended
the stock meta-objects to support some niceties like documentation and
default initialization arguments.  I don't yet have key and optional
args to generic functions but I should be able to get those rolled in
the next night or so.

They are not quite CLOS copies, since I want to avoid alot of the
complexities, and design by commitee features of CLOS.  So I'm prolly
not going to be building method combination abstractions, and more
complex method selection, but I do want to get optional and keys in
the lambda lists of methods, and better debugging.  Also should be
able to copy alot of the utility defines and neato redirection stuff
in the standard-class (slot-missing and the like).

I want to retain the simplicity and clean semantics of tiny-clos
regardless.  But I'll still be raiding PCL 8^)

> STK is a great example of the power of tiny clos.  TK objects have a
> different metaclass, but look just like other STK objects.  Extending this
> idea for guile, i could imagine a MOP that unified things like foreign
> function interface, and remote method invocation.

Hmm, never thought of remote method invocation...  I could see this as
a nice replacement for the YASOS hacks(no harm meant to the authors)
in the guile ILU support.

> The optimization STK uses, last time i checked, was to write tiny clos in
> C, so memoization wins are still possible.  

This is being taken care of presently, take a look in CVS.  Someone is
taking tiny-clos and doing the lower levels in C.  I havent taken a
close look at it, but my code should sit right on top of it with no
problem.


Here is the work I did tonite.  There are some bugs in tiny-clos that
I havent distributed fixes for yet, but you should be able to get this
running no problem.

;;; Copyright 1998 -- Craig Brozefsky <craig@red-bean.com>
;;; Do whatever you want, just don't say it's yours unless people start
;;; complaining about it.
;;;
;;; I've wrapped up tiny-clos 1.7, as it is distributed with STklos into
;;; a guile module.  Didn't change anything there except I exported just
;;; about everything from it without a "%" in front of it's name, including
;;; the helper code.  If you need that, contact me.  Hopefully it will
;;; be included in the next guile, and optimizificationalized.

;;; Obviously this name needs to change, can someone in charge of
;;; module naming please give me a clue.  But anyways, for now it's
;;; not a module because for some god forsaken reason the macro
;;; expander is not seeing the right environment, and therefor my
;;; macros fail unless you are using the (clos-1.7 clos) yourself.  So
;;; for now it's just a (load-from-path) thang'
;;;
;;;(define-module (glos)
;;;  :use-module (clos-1.7 clos))

(use-modules (clos-1.7 clos))

;;; The default meta-class for all classes using GLOS.  It doesn't do much
;;; except add a docstring, and handling of a default-initargs list 
;;; which behaves just like it does in CLOS.  Aren't MOPs just the shit.
(define-public <glos-class>
  (make-class (list <class>)
	      (list (list
		     'docstring
		     #:getter 'describe-class
		     #:setter 'set!-class-docstring
		     #:init-keyword #:documentation)
		    (list
		     'def-initargs
		     #:accessor 'default-initargs
		     #:init-keywords #:default-initargs))))
		    

;;; This defclass is fairly metaclass independent, it just assumes
;;; that you want your class level options placed at the end of your
;;; args.  Basically, it assumes your metaclass behaves something like
;;; <class> or <glos-class>.  Maybe there is some way to formalize these
;;; expectations...
;;; It's basically a clone of the CLOS defclass as far as argument formats
;;; (DEFCLASS (superclass ...) ((slot slotargs ...) ...) . class-options)
(defmacro-public defclass (name supers slots . options)
  (let ((sups (%defclass-process-supers supers))
	(slist (%defclass-process-slots slots))
	(opts (%defclass-process-options options))
	(meta-c (getl options #:metaclass <glos-class>)))
    (eval `(define-public ,name
	     (make ,meta-c 
	       'direct-supers ,sups
	       'direct-slots ,slist
	       ,@opts)))))

;;duh!
(define (%defclass-process-options options)
  options)

(define (%defclass-process-supers supers)
  (if (not (pair? supers))
      (throw 'defclass-error "Superclass argument in wrong format."))
  (cons 'list supers))

; The result of thinking too hard...
;(define (%defclass-process-supers supers)
;  (let loop ((tail supers)
;	     (sups '(list)))
;    (if (pair? tail)
;	(loop (cdr tail) (cons (car tail) sups))
;	(reverse sups))))

(define (%defclass-process-slots slots)
  (let loop ((tail slots)
	     (slist '(list)))
    (if (pair? tail)
	(loop (cdr tail) (cons
			       (if (pair? (car tail))
				   (cons 'list (car tail))
				   (car tail))
			       slist))
	(reverse slist))))

;; It's late, and this is as simple as I can get.
(define (%specializer-from-lambda-list ll)
  (let loop ((param (car ll))
	     (ll (cdr ll))
	     (spec '()))
    (if (and (pair? param)
	     (not (null? ll)))
	(loop (car ll)
	      (cdr ll) (cons (cadr param) spec))
	(reverse (if (pair? param)
		     (cons (cadr param) spec)
		     spec)))))
    
(define (%prototype-from-lambda-list ll)
  (let loop ((param (car ll))
	     (ll (cdr ll))
	     (spec '()))
    (if (not (null? ll))
	(loop (car ll)
	      (cdr ll) (cons (if (pair? param)
				 (car param)
				 param)
			     spec))
	(reverse (cons (if (pair? param)
			   (car param)
			   param)
		       spec)))))


;; This macro works sorta like the CLOS defmethod, except that it's alot
;; dumber.  It does not handle keys or optionals, and can only take
;; tiny-closs class names ala <class> or <object> for type specifiers, and
;; stops building the specializer when it sees a non typed parameter.
;; It automatically bind (call-next-method) in the body for you as
;; well.
(defmacro-public defmethod (method-name lambda-list . body)
  (let ((specializer (%specializer-from-lambda-list lambda-list))
	(proto (cons 'call-next-method
		     (%prototype-from-lambda-list lambda-list))))
    (if (not (and (defined? method-name)
		  (member <generic>
			  (class-cpl (class-of (eval `,method-name))))))
	(eval `(define-public ,method-name (make <generic>))))
    (display specializer)
    (eval `(add-method ,method-name (make-method
				     (list ,@specializer)
				     (lambda ,proto
				       ,@body))))))