Mutable objects in FOOP in development v.10.1.8

Started by Lutz, December 06, 2009, 06:46:14 AM

Previous topic - Next topic

Lutz

Mutable objects in FOOP in development v.10.1.8



Mutable objects in FOOP (Functional Object Oriented Programming) has been the most asked feature for FOOP, and now its here:


(new Class 'Circle)

(define (Circle:move dx dy)
    (inc (self 1) dx)
    (inc (self 2) dy))

(set 'aCircle (Circle 1 2 3))

(:move aCircle 10 20)

aCircle => (Circle 11 22 3)


Now FOOP methods drop the object parameter and the target objects is accessed via the new function 'self'.



For files and complete changes notes see:



http://www.newlisp.org/downloads/development/">http://www.newlisp.org/downloads/development/



See changed FOOP documentation here:



http://www.newlisp.org/downloads/development/newlisp_manual.html#foop">http://www.newlisp.org/downloads/develo ... .html#foop">http://www.newlisp.org/downloads/development/newlisp_manual.html#foop

http://www.newlisp.org/downloads/development/newlisp_manual.html#colon">http://www.newlisp.org/downloads/develo ... html#colon">http://www.newlisp.org/downloads/development/newlisp_manual.html#colon

http://www.newlisp.org/downloads/development/newlisp_manual.html#self">http://www.newlisp.org/downloads/develo ... .html#self">http://www.newlisp.org/downloads/development/newlisp_manual.html#self

itistoday

#1
This is fascinating, and I can't help but think that we both should have communicated regarding what we were doing.



Since our email exchange I've created an object-oriented system for newLISP that I'll announce today, it has several advantages over FOOP, even this redesigned version. In short it allows you to do real object-oriented programming with reference passing, and is backwards compatible with many old versions of newLISP. :-)



I.e., it solves this problem with FOOP (which exists even with this updated version):


(setf my-obj (MyFOOPClass 1 2 3))

(define (func-1 obj)
(func-2 obj)
)

(define (func-2 obj)
(:modify obj 54)
obj
)

(func-1 obj)

obj ; obj remains unchanged here


But this is still a very welcome change to FOOP, as I'm assuming the use of the 'self' function makes it more efficient too!
Get your Objective newLISP groove on.

Lutz

#2
When you wrap a destructive function (:move ...) with a non-destructive one (func-1 and func-2), then of course the result is non-destructive. But 'obj' will be modified after excecution of (:modify ...). One must stay in the paradigm of either FOOP or normal .



Yes, the new 'self' makes FOOP also faster, less overhead internally and less newLISP code to write. Before when modifying objects, you always had to return the modified object and then do a re-assignment on the higher level, which now is not necessary anymore.

itistoday

#3
Quote from: "Lutz"When you wrap a destructive function (:move ...) with a non-destructive one (func-1 and func-2), then of course the result is non-destructive. But 'obj' will be modified after excecution of (:modify ...). One must stay in the paradigm of either FOOP or normal.


Sure, I'm just saying that this still doesn't allow full-on object-oriented programming (which is something that I need for that database wrapper, but thankfully I found an excellent solution).



Note to Dragonfly users: don't upgrade to this yet, as Dragonfly makes use of the old-style FOOP.



Edit]
Get your Objective newLISP groove on.

itistoday

#4
When used in this way, Functional-OOP becomes a misnomer. ;-p
Get your Objective newLISP groove on.

m i c h a e l

#5
I've wondered about this ever since I first toyed around with mutability in my experiments (BTW, bravo on self, Lutz! Perfectly simple and fits newLISP to a tee.). Surely, if the 'F' in FOOP means anything, it must be immutable objects, right? I don't know. I've tried to approach FOOP without preconceptions or prejudices. Still, I've tried hard to find a good way to add mutability to FOOP. Unchangeable objects would appear to be an oxymoron. When I think of objects, I think of little self-manipulating packages of data.



If FOOP is to lose its 'F', perhaps it should be called newOOP instead ;-)



m i c h a e l

TedWalther

#6
Cool.  It looks like "self" is doing what I was hoping for with "this".  I'll go refresh myself on FOOP again.  Things are calming down at work again; perhaps over Christmas break I can power through and fix up configure-alt enough to be a suitable replacement for configure, and get a first release of the OpenBSD port/package done.  Are there any outstanding issues with configure-alt, Lutz?  Also, the OpenBSD test environment is now upgraded to version 4.6.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence.  Nine months later, they left with a baby named newLISP.  The women of the ivory towers wept and wailed.  \"Abomination!\" they cried.

Lutz

#7
The fact that each object in FOOP is the functional expression which generates itself - its own constructor expression - still justifies the 'F' in FOOP.


(new 'Class 'Obj)
(Obj 1 2 3) ;=> (Obj 1 2 3)
(eval (Obj 1 2 3)) ;=> (Obj 1 2 3)
(apply (first (Obj 1 2 3)) (rest (Obj 1 2 3))) ;=> (Obj 1 2 3)


The same is true for nested objects, they are nested constructor functions:


(new Class 'Foo)
(new Class 'Bar)
(set 'x 1 'y 2 'z 3)
(Foo (Bar x y z) (Bar x y z)) ;=> (Foo (Bar 1 2 3) (Bar 1 2 3))
(Foo (Bar 1 2 3) (Bar 1 2 3)) ;=> (Foo (Bar 1 2 3) (Bar 1 2 3))


So the object-oriented programming in FOOP may not be full functional, but the object is. Not functional object-oriented programming but functional-object oriented programming.





ps -> Ted: I don't think there are any outstanding issues in configure-alt at the moment.

TedWalther

#8
How is (new) different from (copy)?  Is object an internal datatype the way contexts and red-black dictionaries are?
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence.  Nine months later, they left with a baby named newLISP.  The women of the ivory towers wept and wailed.  \"Abomination!\" they cried.

cormullion

#9
This looks cool, but ... Can you explain why in your code


(define (Circle:move dx dy)
    (inc (self 1) dx)
    (inc (self 2) dy))

(set 'aCircle (Circle 1 2 3))

(:move aCircle 10 20)

aCircle => (Circle 11 22 3)


you  pass three numbers with Circle, yet only the first two are modified, and yet they're referred to as (self 1) and (self 2), so these aren't indexes? But:


QuoteOld FOOP methods have to be rewritten to drop the obj parameter:



    old : (define (Foo:method obj p1 p2) ...)

    new : (define (Foo:method p1 p2) ...)


looks like I'd want to modify 0 and 1, not 1 and 2.



As I said, confused a bit here... :)

Lutz

#10
It is less confusing if we describe the code in original object oriented terminology (all in italic font). The following function call is called a message call in OO:


(:move aCircle 10 20)

:move is the message sent to the object aCircle and 10 and 20 are the message parameters, only two of them in this example. In OO Java or JavaScript you would write aCircle.move(10, 20).



The object aCircle encapsulates the object data and the methods how to process the data. So when we receive the call, we really are inside the object aCircle and can refer to it with (self). There are only two parameters in the :move message, in this case 10 and 20 for the dx and dy method parameters.



The function (self) returns the object with the Class Id at index 0 and values for x-position, y-position and radius at index 1,2 and 3. In out example (self) is aCircle, the receiver of the message


(define (Circle:move dx dy)
    (inc (self 1) dx)
    (inc (self 2) dy))


Because all the shapes of Class Circle use the same methods, we don't have to carry them around in each object instance of that Class, but can just point to with a Class Id, which is the first thing in the aCircle object  (Circle 1 2 3).



Note that :move is not the same thing as Circle:move. :move is the message sent, and Circle:move is the method processing the message  received by the object aCircle which is an instantiation of Class Circle. The message :move is polymorph and depending on the object Class it may point to different methods, there could also be a Rectangle:move method or Triangle:move method.

m i c h a e l

#11
I started this response before Lutz answered you, cormullion, but I'll post it anyway :-)



I think Circle's third attribute is its radius, which, of course, would not be affected by moving the circle.



The indexes used in self represent the positions of the attributes within an object. The first element of an object is the class (at index 0). In the case of Circle, x is 1, y is 2, and the radius is 3.



Most object-oriented languages don't specifically declare an argument for the object passed to a method (Python being one exception). They use some form of pseudo-variable, usually self or this, that can only be used within a method (you'll notice calling newLISP's self outside of a method will always return nil).



Just remember that self refers to the object that received the message, not the arguments of the method.



m i c h a e l

TedWalther

#12
Thanks, I've been wanting to use context-objects more extensively, with millions of items of the same class type, but wasn't sure how to do it without duplicating the methods for every single object instance.  It looks like there is a way after all, even if I don't fully grokk it yet.  Hurray!  I won't get out of memory errors after all.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence.  Nine months later, they left with a baby named newLISP.  The women of the ivory towers wept and wailed.  \"Abomination!\" they cried.

Lutz

#13
There was a newlisp-10.1.8-foop.tgz in the development directory, whoever downloaded this, throw it away, it is an older version. The correct version containing the FOOP changes talked about in this thread is: newlisp-10.1.8.tgz.

cormullion

#14
Ah. Great - I wasn't thinking straight... Thanks both.



Mostly I use FOOP mainly in timeutilities.lsp at the moment. I've converted it over to the new style and it seems to work fine.



One thing you could advise on: I have a method called :shift which adds time to a time object. In the old style FOOP I was happy enough with the clarity and restriction of immutability - it made sense for 'fixed' times (if not for appointments):


>; older style FOOP
(set 'xmas (Time 2009 12 25))
(Time 1261699200 0)
> (set 'boxing-day (:shift xmas 1 'days))
(Time 1261785600 0)
> (:represent xmas)
"Friday December 25 2009 00:00:00"
> (:represent boxing-day)
"Saturday December 26 2009 00:00:00"


With the new style, doing this also shifts xmas day - objects being no longer immutable. So I tried putting in a flag at the end of the :shift method call such that you could say either 'please modify the object' or not. So, with f as the increment, the shift method ends like this:


(if modify-flag
      (begin
         (setf (self 1) (add f (self 1)))
         (self)) ; return the modified original object
      (begin
         (set 'new-time (self))
         (setf (new-time 1) (add f (new-time 1)))
         new-time ; return the modified copy
))))


Is this the best way to do this? It appears to work, but...