More Mutable FOOP

Pondering the philosophy behind the language
Locked
m i c h a e l
Posts: 394
Joined: Wed Apr 26, 2006 3:37 am
Location: Oregon, USA
Contact:

More Mutable FOOP

Post by m i c h a e l »

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.

Code: Select all

(set (global 'extend) (lambda-macro (sig)
	(eval 
		(cons 'define (cons sig 
			(expand (args) (list (list (sig 0) (eval (sig 0)))))
		))
	)
))
So what does this code allow us to do? Here’s an example:

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
> _
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:

Code: Select all

(set (global ‘obj) (fn (class that) (if that (setq class:obj that) class:obj)))
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:

Code: Select all

(extend (Class:Class) (obj (context) (apply Class:Class (args))))
Now, we can use objects this way:

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")
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:

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)
	)
)
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:

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))
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:

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"
> _
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:

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
	)
))
Now we can do the following:

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")
> _
There are two ways to work with the named objects: in one step or two.

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.")
> _
Now, one step:

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.")
> _
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

m i c h a e l
Posts: 394
Joined: Wed Apr 26, 2006 3:37 am
Location: Oregon, USA
Contact:

Post by m i c h a e l »

I forgot to include the object? predicate used by the second version of the obj function:

Code: Select all

(set (global 'object?) (fn (it)
	(and 
		(list? it)
		(not (empty? it))
		(context? (it 0))
	)
))
m i c h a e l

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Post by cormullion »

This is getting difficult... Good stuff, though! :)

m i c h a e l
Posts: 394
Joined: Wed Apr 26, 2006 3:37 am
Location: Oregon, USA
Contact:

Post by m i c h a e l »

cormullion wrote:This is getting difficult...
Uh-oh, I thought I was making it easier! I’ve abandoned using named objects for now (the updated obj function), but my current experiments have been very promising.

I'm also working on a useable define-keyword macro. It functions the way I would want keyword arguments to work, but it still has a long way to go before it can be used in real code.
cormullion wrote:Good stuff, though! :)
Thank you. I hope to model something really complex in FOOP someday. I’ve already managed to dwarf all of the models I made in other languages BnL (Before newLISP). But I still have more to discover before I can model even a simple kitchen appliance with any degree of realism. My true interest in OOP is simulation, for which it is best-suited. newLISP is proving an amazing platform for programming paradigm exploration. I’m very encouraged by the progress I’ve made.

m i c h a e l

itistoday
Posts: 429
Joined: Sun Dec 02, 2007 5:10 pm
Contact:

Post by itistoday »

I love these experiments, and hopefully I will get some time to play with this soon... Thanks!
Get your Objective newLISP groove on.

Locked