The earlier technique required both kinds of objects because the contained objects needed to be immutable. Only the outside object could be mutable. This outer object controlled all of the little immutable objects within itself. If you wanted your outer object to become a part of another object—thus becoming an inner object—you would need to convert the formerly mutable object into an immutable one.
While not egregious, this complexity does make that technique less attractive. So on I continued into the mutable FOOP unknown.
Then one day, I began to store the object results of all methods (including constructors) in a variable within the appropriate classes. For example, sending :+ to two Points would not only give you the result, but it would store that result in Point:obj, as well. This would also allow multiple objects of different classes to be returned (removing a jar lid would leave the jar in Jar:obj and the lid in Lid:obj).
So how does this new way work? Like this:
First, we extend . . . oh wait. I haven’t shown you the extend macro yet.
Code: Select all
(set (global 'extend) (lambda-macro (sig)
(eval
(cons 'define (cons sig
(expand (args) (list (list (sig 0) (eval (sig 0)))))
))
)
))
Code: Select all
> (new Class 'Point)
Point
> (Point)
(Point)
> (extend (Point:Point (x 0) (y 0)) (Point:Point x y))
(lambda ((x 0) (y 0)) ((lambda () (cons (context) (args))) x y))
> (Point)
(Point 0 0)
> (Point 10)
(Point 10 0)
> (Point 10 20)
(Point 10 20)
> (Point 10 20 30)
(Point 10 20)
> ; or just regular functions (not built-ins)
> (define (f n) (* n n))
(lambda (n) (* n n))
> (f 5 5)
25
> (extend (f n (n2 0)) (+ (f n) n2))
(lambda (n (n2 0)) (+ ((lambda (n) (* n n)) n) n2))
> (f 5 5)
30
> ; you can keep going, but be careful
> (extend (f n (n2 0) (n3 0)) (+ (f n n2) (* n3 2)))
(lambda (n (n2 0) (n3 0)) (+ ((lambda (n (n2 0)) (+ ((lambda (n) (* n n)) n) n2))
n n2)
(* n3 2)))
> (f 5 5)
30
> (f 5 5 5)
40
> (f 5)
25
> _
First, we extend . . . just a minute. We need one more function before we can extend Class:Class. A function called obj that gets and sets a class’s obj variable:
Code: Select all
(set (global ‘obj) (fn (class that) (if that (setq class:obj that) class:obj)))
With this, we can finally extend Class:Class:
Code: Select all
(extend (Class:Class) (obj (context) (apply Class:Class (args))))
Code: Select all
> (new Class 'Author)
Author
> (extend (Author:Author (last-name "") (first-name "")) (Author:Author last-name first-name))
(lambda ((last-name "") (first-name "")) ((lambda () (obj (context) (apply (lambda
()
(cons (context) (args)))
(args)))) last-name first-name))
> (Author)
(Author "" "")
> (Author "Heller" "Joseph")
(Author "Heller" "Joseph")
> (obj Author)
(Author "Heller" "Joseph")
Code: Select all
(define (Class:access it idx value allow-nil?)
(if (or value allow-nil?)
(begin (setf (it idx) value) (obj (it 0) it))
(it idx)
)
)
With this final piece in place, we can write Author’s accessors:
Code: Select all
(define (Author:last-name it last-name) (:access it 1 last-name))
(define (Author:first-name it first-name) (:access it 2 first-name))
Code: Select all
> (Author "" "Jerome")
(Author "" "Jerome")
> (:last-name (object Author) “Salinger")
(Author "Salinger" "Jerome")
> (:first-name (obj Author) "J.D.")
(Author "Salinger" "J.D.")
> (define (Author:string it) (string (:first-name it) " " (:last-name it)))
(lambda (it) (string (: first-name it) " " (: last-name it)))
> (:string (obj Author))
"J.D. Salinger"
> _
For working with multiple objects, as mentioned earlier, the obj function could be extended to accommodate named multiple objects. In the following scheme, we use a Tree called Obj to store the objects:
Code: Select all
(new Tree ‘Obj)
(set (global 'obj) (fn (a b)
(if
(string? a) (if (object? b) (Obj a b) (Obj a))
(object? a) (Obj b a)
(string? b) (Obj b a:obj)
(a:? b) (setq a:obj b)
(nil? b) a:obj
)
))
Code: Select all
> (Author "Heller" "Joseph")
(Author "Heller" "Joseph")
> (obj Author "jh")
(Author "Heller" "Joseph")
> (obj (Author "Serling" "Rod") "rs")
(Author "Serling" "Rod")
> (obj "rs")
(Author "Serling" "Rod")
> (obj "jh")
(Author "Heller" "Joseph")
> _
Two steps:
Code: Select all
> (obj (Author "Salinger" "Jerome") "jds")
(Author "Salinger" "Jerome")
> (:first-name (obj "jds") "J. D.")
(Author "Salinger" "J. D.")
> (obj Author "jds")
(Author "Salinger" "J. D.")
> (obj "jds")
(Author "Salinger" "J. D.")
> _
Code: Select all
> (obj (Author "Salinger" "Jerome") "jds")
(Author "Salinger" "Jerome")
> (obj "jds" (:first-name (obj "jds") "J. D."))
(Author "Salinger" "J. D.")
> (obj "jds")
(Author "Salinger" "J. D.")
> _
That’s it for this report. Stay tuned for further developments.
m i c h a e l