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]

GOOS v.0.3


Here is yet another version of GOOS.  This time it is
implemented without modules.  This version needs the
variable-bind-pair procedure I have been talking about.
However one could go in a hack out this dependency, but
you will lose eval-in-object and you will have to
modify make-class (simple mods).

I apologize about not having a proper print callback
function, but there does not seem to be any hook into
it without resorting to use structures or records, both
of which I do not want to use.  See class-print, et. al.
for details.

The examples have also changed to add a class name
argument.

Wade

;; E X A M P L E S

;; Simple Class

(define person
  (make-class 'person '()
     (define *class-slots* '(all-persons))
     (define *instance-slots* '(name age height weight))
     (define all-persons '())
     (define initialize-instance
       (lambda (inst . args)
	 (initargs->slot-define! inst args 'name :name)
	 (initargs->slot-define! inst args 'age :age 'na)
	 (initargs->slot-define! inst args 'height :height 'na)
	 (initargs->slot-define! inst args 'weight :weight 'na)))))

(define wade (make-instance person :name 'wade :age 38 :height 171 :weight 70))
(define arline (make-instance person :name 'arline :age 38 :height 173 :weight 60))

;; Class with inheritance

(define elderly-person
  (make-class 'elderly-person (list person)
     (define *class-slots* '())
     (define *instance-slots* '(retired))
     (define initialize-instance
       (lambda (inst . args)
	 (initargs->slot-define! inst args 'retired :retired)))))

(define dad (make-instance elderly-person 
			   :retired #t
			   :name 'dad 
			   :age 78 
			   :height 190 
			   :weight 90))


;; Class with multiple inheritance

(define wise-person
  (make-class 'wise-person (list person elderly-person)
     (define *class-slots* '())
     (define *instance-slots* '(area-of-wisdom))
     (define initialize-instance
       (lambda (inst . args)
	 (initargs->slot-define! inst args 'area-of-wisdom :area-of-wisdom)))))

(define yasmin (make-instance wise-person 
			      :name 'yasmin-manji
			      :age 45 
			      :retired 'fat-chance
			      :area-of-wisdom 'yoga))

(define bo (make-instance wise-person 
			      :name 'bo-lozoff
			      :retired 'never
			      :area-of-wisdom 'prison-system))

------------------ GOOS -------------------------------------------------------

;; GOOS v.0.3 for Guile. This version of GOOS is
;; implemented without modules.  However some of it
;; is inspired by the Guile module system.
;
;; This code is freely given to the FSF.

(read-set! keywords 'prefix)

;; Object =====>  (vector *object-tag* specialization name parents slots)
;; specialization - objects can be created that have specialties such as
;;                  records, classes and instances.  Many extended types could
;;                  be constructed out of objects.
;; name - anything is possible
;; parents - list of parents (objects)
;; slots - stored as an association list
;;
;; Basic object accessors

(define *object-tag* "object")

(define (make-object specialization name parents . initial-slots)
  (vector
   *object-tag*
   specialization
   name
   (begin
     (for-each
      (lambda (parent) 
	(if (not (object? parent)) 
	    (error "Object Parent Must Be Object:" parent)))
      parents)
     parents)
   initial-slots))

(define (object? obj) (and (vector? obj) (eq? *object-tag* (vector-ref obj 0))))
(define (object-specialization obj) (vector-ref obj 1))
(define (object-name obj) (vector-ref obj 2))
(define (object-parents obj) (vector-ref obj 3))
(define (object-slots obj) (vector-ref obj 4))
(define (object-slot/value-add! obj slot/value)
  (vector-set! obj 4 (cons slot/value (object-slots obj))))

(define object-local-slot/value-ref
   (lambda (obj sym)
     (assq sym (object-slots obj))))

(define object-apply-down-inheritance
  (lambda (obj func)
    (define traversed-inheritance '())
    (define (apply-in-object obj)
      (if (not (memq obj traversed-inheritance))
	  (begin
	    (func obj)
	    (for-each
	     (lambda (parent)
	       (apply-in-object parent))
	     (object-parents obj))
	    (set! traversed-inheritance (cons obj traversed-inheritance)))))
    (apply-in-object obj)))

(define object-apply-up-inheritance
  (lambda (obj func)
    (define traversed-inheritance '())
    (define (apply-in-object obj)
      (if (not (memq obj traversed-inheritance))
	  (begin
	    (for-each
	     (lambda (parent)
	       (apply-in-object parent))
	     (reverse (object-parents obj)))
	    (func obj)
	    (set! traversed-inheritance (cons obj traversed-inheritance)))))
    (apply-in-object obj)))

(define object-inheritance-list
  (lambda (obj)
    (define inheritance-list '())
    (object-apply-up-inheritance
     obj
     (lambda (obj)
       (set! inheritance-list (cons obj inheritance-list))))
    (cdr inheritance-list)))

(define (object-slot/value-ref obj sym)
  (catch 'object-slot/value-ref
	 (lambda ()
	   (object-apply-down-inheritance
	    obj
	    (lambda (parent)
	      (let ((slot/value (object-local-slot/value-ref parent sym)))
		(if slot/value
		   (throw 'object-slot/value-ref slot/value)))))
	   #f)
	 (lambda (sym slot/value . rest) slot/value)))

(define (object-define! obj sym val)
  (let ((slot/value (object-local-slot/value-ref obj sym)))
    (if slot/value
	(set-cdr! slot/value val)
	(begin
	  (set! slot/value (cons sym val))
	  (object-slot/value-add! obj slot/value)))
    slot/value))

(define (object-ref obj sym)
  (let ((slot/value (object-slot/value-ref obj sym)))
    (if slot/value
	(cdr slot/value)
	(error "No Slot Defined On Object:" sym))))

(define (object-set! obj sym val) 
  (let ((slot/value (object-slot/value-ref obj sym)))
    (if slot/value
	(set-cdr! slot/value val)
	(error "No Slot Defined On Object:" sym))))

(define (object-defined? obj sym)
  (if (object-slot/value-ref obj sym) #t #f))


;; Procedures for evaluation
(define object-lookup-closure
  (lambda (obj)
    (lambda (sym define?)
      (if define?
	  (variable-bind-pair (object-define! obj sym #f))
	  (let ((slot/value (object-slot/value-ref obj sym)))
	    (if slot/value
		(variable-bind-pair slot/value)
		(*top-level-lookup-closure* sym define?)))))))

(define (eval-in-object exp obj)
  (eval2 exp (object-lookup-closure obj)))

(define (eval-all-in-object elist obj)
  (for-each
   (lambda (exp) 
     (eval2 exp (object-lookup-closure obj)))
   elist))


;; Start of class procedures

;; (make-class name supers . expressions)
(define make-class
  (procedure->macro
   (lambda (exp env)
     `(let ((%%class%% (make-object 'class ,(cadr exp)
			(if (null? ,(caddr exp))
			    (list the-standard-class)
			    ,(caddr exp)))))
	(eval-all-in-object ',(cdddr exp) %%class%%)
	%%class%%))))

(define (make-instance class . inits)
  (let ((inst (make-object 'instance (class-name class) (list class))))
    (apply (slot-ref inst '%meta-initialize-instance-proc) (cons inst inits))
    inst))

(define slot-ref object-ref)
(define slot-set! object-set!)
(define slot-define! object-define!)
(define slot-defined? object-defined?)
(define (slot-locally-defined? obj sym)
  (let ((s (object-local-slot/value-ref obj sym)))
    (if s #t #f)))
(define (class-name obj) (object-name obj))
(define slots object-slots)
(define supers object-parents)
(define (describe obj) ((slot-ref obj 'describe) obj))
(define (class? obj) (and (object? obj) (eq? (object-specialization obj) 'class)))
(define (instance? obj) (and (object? obj) (eq? (object-specialization obj) 'instance)))
(define (class-print obj) ((slot-ref obj '*printer*) obj))
(define inheritance-list object-inheritance-list)

(define (class-help obj)
  (if (slot-locally-defined? obj '*documentation-string*)
      (slot-ref obj '*documentation-string*)
      "No Help Available"))

;; Initialization Args - 
;; Init args are of the form (<keyword> value <keyword> value ...)

(define (get-initarg key arglist)
  (let ((arg (memq key arglist)))
    (if arg
	(cadr arg)
	(error "Arg does not exist with key: " key))))

(define (initarg-in-list? key arglist)
  (if (memq key arglist) #t #f))

(define (initargs->slot-define! obj args sym key . default)
  (let ((arg (memq key args)))
    (if arg
	(slot-define! obj sym (cadr arg))
	(if (null? default)
	    (error "Missing required arg in initialization: " key)
	    (slot-define! obj sym (car default))))))

;; The Standard Class with Meta-Object Protocol (Methods)
;; for introspection and initialization of instances.  Classes
;; are always objects within this system.

(define the-standard-class
  (make-object 'class 'the-standard-class '()

     (cons '*printer*
	   (lambda (obj . port)
	     (if (null? port) (set! port (current-output-port)))
	     (if (class? obj)
		 (display "#<class " port)
		 (display "#<instance " port))
	     (write (class-name obj) port)
	     (display ">" port)))
		 
     (cons 'describe 
       (lambda (obj)
	 (newline)
	 (display "slots: ") (write (slots obj)) (newline)
	 (display "supers: (") 
	 (for-each
	  (lambda (parent)
	    (class-print parent) (display #\space))
	  (object-inheritance-list obj))
	 (display ")")
	 (newline)))

     (cons '%meta-initialize-instance-proc
       (lambda (inst . inits)
	 (define inst-inits (cons inst inits))
	 (object-apply-up-inheritance
	  inst
	  (lambda (class)
	    (let ((initialize-method
		   (object-local-slot/value-ref class 'initialize-instance)))
	      (if initialize-method
		  (apply (cdr initialize-method) inst-inits)))))))

     (cons 'initialize-instance 
       (lambda (inst . args) #t))
     )
)