More Mutable FOOP
Posted: Fri Jul 10, 2009 3:50 am
In my continued experiments with mutability in FOOP, I've discovered another technique that simplifies and unifies the previous system. It’s done without the use of symbols stored in the MAIN context and doesn’t require two types of objects (mutable and immutable).
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.
So what does this code allow us to do? Here’s an example:
With this piece in our toolbox, we can continue on.
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:
While this function is not strictly necessary, it does combine getting and setting, so it hides a little bit of detail (and as we’ll see later, obj can be extended to handle multiple named objects).
With this, we can finally extend Class:Class:
Now, we can use objects this way:
Just one more piece needs to be redefined for the whole thing to work easily, and that’s the access method we defined previously for the now-abandoned use of symbols:
There are only subtle changes made to :access to account for the new technique.
With this final piece in place, we can write Author’s accessors:
Now, as long as we use the object stored in the class variable obj (through the matching obj function), we can treat objects as though they are mutable:
So far, I have found this to be quite natural in use.
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:
Now we can do the following:
There are two ways to work with the named objects: in one step or two.
Two steps:
Now, one step:
I’ve only started using this modified version of obj recently, so we’ll see how it turns out.
That’s it for this report. Stay tuned for further developments.
m i c h a e l
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