A simple minimal OO system for newLISP

Started by Lutz, October 23, 2007, 03:57:40 PM

Previous topic - Next topic

Fanda

#15
This version allows for more parameters:
(define-macro (: _func _obj)
   (let (_data (eval _obj))
      (apply (sym _func (_data 0)) (cons _data (args)))))


But... I am still having problems with "setters" (methods). Can anyone implement circle:set-xy or circle:set-radius???

Lutz

#16
setters are not possible this way when using the : macro, its pure functional. But you could do the following:


(set 'mypoint (:move mypoint 3 4))


Lutz



ps: thanks for adding the missing additional parameters

m i c h a e l

#17
Based on Lutz's last response, I've rewritten the Elica example in the FOO (functional object-oriented) style. Before I could get it to work, though, I needed to modify the : macro:


(define-macro (: _func _obj)
(let (_data (eval _obj))
(apply (sym _func (_data 0)) (cons _data (map eval (args))))
)
)


(load "colon.lsp") ; this contains the ':' macro

;; D I S P L A Y A B L E
(define (displayable:string obj) (string obj))
(define (displayable:print obj) (println ((context (obj 0) 'string) obj) ""))

;; P O I N T
(new displayable 'point)
(define (point:point (x 0) (y 0)) (list point x y))
(define (point:string pt) (string (pt 1) "@" (pt 2)))
(define (point:move pt dx dy) (point (+ dx (pt 1)) (+ dy (pt 2))))
(define (point:oper op ags)
  (cons point (apply map (cons op (map (fn (e) (1 e)) ags))))
)
(define (point:+) (point:oper + (args)))
(define (point:-) (point:oper - (args)))
(define (point:*) (point:oper * (args)))

;; S E G M E N T
(new displayable 'segment)
(define (segment:segment (a (point)) (b (point))) (list segment a b))
(define (segment:string sg) (string (:string (sg 1)) " to " (:string (sg 2))))
(define (segment:move sg ax ay bx by)
  (list segment (:move (sg 1) ax ay) (:move (sg 2) bx by))
)

;; T R I A N G L E
(new displayable 'triangle)
(define (triangle:triangle (ab (segment)) (bc (segment)) (ca (segment)))
  (list triangle ab bc ca)
)
(define (triangle:string tr)
  (string (:string (tr 1)) ", " (:string (tr 2)) ", " (:string (tr 3)))
)
(define (triangle:move tr sg ax ay bx by)
  (set-nth sg tr (:move (tr sg) ax ay bx by))
)

;; S A M P L E   R U N
(:print (set 'a (point)))
(:print (set 'b (point 20 0)))
(:print (set 'c (point 10 5)))
(:print (set 'tri (triangle (segment a b) (segment b c) (segment c a))))

(println "Move 'bc' segment of 'tri' by (30 5) (20 10):")
(set 'tri (:move tri 2 30 5 20 10))
(:print tri)

(println "Point addition, subtraction, and multiplication:")
(:print (point:+ a b c))
(:print (point:- a b c))
(:print (point:* (point 2 43) (point 22 1) c))

;; E N D


Here's the output:


0@0
20@0
10@5
0@0 to 20@0, 20@0 to 10@5, 10@5 to 0@0
Move 'bc' segment of 'tri' by (30 5) (20 10):
0@0 to 20@0, 50@5 to 30@15, 10@5 to 0@0
Point addition, subtraction, and multiplication:
30@5
-30@-5
440@215


m i c h a e l

Lutz

#18
I finished implementing the colon : macro natively and it runs your point/segment/triangle and polymorphism example code with identical results :-)



Lutz

cormullion

#19
A few questions from someone struggling to keep up ... :)



When i try to run your example, michael, I get this error:


string expected in function context : 'MAIN:string
called from user defined function point:print


which is:



(define (displayable:print obj)  (println ((context (obj 0) 'string) obj) ""))


I suppose?



What is the 'displayable' here, anyway? Some kind of abstract class, I expect?



And can (:print tri) be read as "applying the method :print to the 'tri' object, which is an instance of class 'triangle', which was created by (set 'tri (triangle (segment a b)...".... (or something similar)?

m i c h a e l

#20
Hi Cormullion!



I wondered when you were going to join the party ;-)



I'm not sure why you are getting that error. I re-ran my local copy and even copied it again from the above post (in case it was a formatting issue), and neither produced any errors. Lutz also seems to be able to run it correctly.



Wait! I bet you're using an earlier version of newLISP (before symbols were allowed in context), so changing the following:


(define (displayable:print obj)  (println ((context (obj 0) 'string) obj) ""))


to


(define (displayable:print obj)  (println ((context (obj 0) "string") obj) ""))


should fix it.


Quote from: "Cormullion"What is the 'displayable' here, anyway? Some kind of abstract class, I expect?


I'm using it as a mixin. What this is saying is: A point, segment, and triangle are displayable. (Really, I was just too lazy to write a print method for each data type ;-)


Quote from: "Cormullion"And can (:print tri) be read as "applying the method :print to the 'tri' object, which is an instance of class 'triangle',


Yes, but to be a little more exact, I would say "polymorphically apply the print method to the triangle object referenced by the symbol tri." Polymorphic is just a fancy word for objects responding to messages according to their type. Notice that :print actually ended up calling point:print. print is really just the first argument to :, which resolves the call by extracting the type from the object (the first element).



Feel free to pick my brain!



m i c h a e l

m i c h a e l

#21
Here's a complex example ;-)


(load "colon.lsp") ; soon, this won't be necessary ;-)

(define (complex:complex (r 0) (i 0)) (list complex r i))
(define (complex:rad c)
(set 're (c 1) 'im (c 2))
(sqrt (add (mul re re) (mul im im)))
)
(define (complex:theta c) (atan (div (c 1) (c 2))))
(define (complex:add a b) (complex (add (a 1) (b 1)) (add (a 2) (b 2))))
(define (complex:mul a b)
(set
'a.re (a 1) 'a.im (a 2)
'b.re (b 1) 'b.im (b 2)
)
(complex
(sub (mul a.re b.re) (mul a.im b.im))
(add (mul a.re b.im) (mul a.im b.re))
)
)
(define (complex:square c) (:mul c c))


Here is an example of use:


> (set 'c1 (complex 34.2 54.2))
(complex 34.2 54.2)
> (set 'c2 (complex 19.8 73.9))
(complex 19.8 73.9)
> (:add c1 c2)
(complex 54 128.1)
> (:mul c1 c2)
(complex -3328.22 3600.54)
> (:rad c1)
64.08806441
> (:theta c1)
0.5628996527
> (:square c2)
(complex -5069.17 2926.44)
> _


I have no idea if I'm using these complex numbers correctly or not :-)



complex still needs sub and div methods, but I've already been distracted by wanting to model the soda-dispensing machine from my Booch book!



m i c h a e l

cormullion

#22
Aha - I'm using 9.2.3 - I'd forgotten I was one step behind. You get used to it.



I've been at this party since it started - but I'm just hanging around near the drinks table being inconspicuous, rather than dancing naked in the middle of the room...



I'll read up about mixins, see if it makes more sense.



Currently ':print tri' feels slightly backwards - but that might change as I think about it some more.



Thanks for the explanations...!

newdep

#23
This OO issue smells like fresh baked cookies... ;-)







..Cant wait to eat them ;-)
-- (define? (Cornflakes))

m i c h a e l

#24
Quote from: "Cormullion"Currently ':print tri' feels slightly backwards - but that might change as I think about it some more.


Think of it as leaving off the context so the specific method can be determined dynamically. We have point:print, segment:print, and triangle:print. When we leave off the context, as in (:print some-obj), the : macro determines which method to call for us, based on the object passed as the second argument (remember, print is the first argument, even though there is no space between the : and print). If you know for sure an object will always be of a certain type, you can (and should?) call the method directly: (point:print a-point), for example. You only need polymorphism when you are unsure of the type of the object beforehand or you have a list of objects of differing types, each having a method defined with the same name (print in this case).


Quote from: "Cormullion"I'll read up about mixins, see if it makes more sense.


Mixins are just another form of inheritance, used in languages that have only single, rather than multiple inheritance. Ruby uses mixins, for example. Think of them as abilities that can be mixed into objects in various combinations. In my example, I want to give the objects the ability to display themselves, so I mix in displayable. Now any object with this mixed in can display itself and can even override displayable's string method to display itself in an object-sepecific way (as I did with point, segment, and triangle).


Quote from: "Cormullion"I've been at this party since it started - but I'm just hanging around near the drinks table being inconspicuous, rather than dancing naked in the middle of the room...


:-) You'll warn us before you get the urge, won't you, Cormullion? There may be children present!



m i c h a e l

m i c h a e l

#25
The following is dedicated to Norman ;-)


newLISP v.9.2.4 on OSX UTF-8, execute 'newlisp -h' for more info.

> (define (box:box (contents '()) (design "Plain brown box")) (list box contents design))
(lambda ((contents '()) (design "Plain brown box")) (list box contents
  design))
> (define (cereal:cereal (substance "corn") (kind "flakes")) (list cereal substance kind))
(lambda ((substance "corn") (kind "flakes")) (list cereal substance
  kind))
> (define (bowl:bowl (contents '()) (kind "cereal")) (list bowl contents kind))
(lambda ((contents '()) (kind "cereal")) (list bowl contents kind))
> (define (spoon:spoon (material "silver") (kind "soup")) (list spoon material kind))
(lambda ((material "silver") (kind "soup")) (list spoon material
  kind))
> (define (put obj place) ((place 0) (cons obj (place 1)) (place 2)))
(lambda (obj place) ((place 0) (cons obj (place 1)) (place 2)))
> (put (spoon) (bowl))
(bowl ((spoon "silver" "soup")) "cereal")
> (define (table:table (top '()) (kind "kitchen")) (list table top kind))
(lambda ((top '()) (kind "kitchen")) (list table top kind))
> (put (spoon) (table))
(table ((spoon "silver" "soup")) "kitchen")
> (define (pour container location) ((location 0) (cons (container 1) (location 1)) (location 2)))
(lambda (container location) ((location 0) (cons (container 1) (location
    1))
  (location 2)))
> (pour (box (cereal)) (bowl))
(bowl ((cereal "corn" "flakes")) "cereal")
> (put (spoon) (pour (box (cereal)) (bowl)))
(bowl ((spoon "silver" "soup") (cereal "corn" "flakes")) "cereal")
> (put (put (spoon) (pour (box (cereal)) (bowl))) (table))
(table ((bowl ((spoon "silver" "soup") (cereal "corn" "flakes"))
   "cereal")) "kitchen")
> (define (bottle:bottle (contents '()) (kind "glass")) (list bottle contents kind))
(lambda ((contents '()) (kind "glass")) (list bottle contents kind))
> (define (milk:milk (from "cow") (kind "2%")) (list milk from kind))
(lambda ((from "cow") (kind "2%")) (list milk from kind))
> (bottle (milk))
(bottle (milk "cow" "2%") "glass")
> (put (pour (bottle (milk)) (put (spoon) (pour (box (cereal)) (bowl)))) (table))
(table ((bowl ((milk "cow" "2%") (spoon "silver" "soup") (cereal
     "corn" "flakes")) "cereal")) "kitchen")
> ;; Breakfast is ready!
> _


I told you I think in objects ;-)



m i c h a e l

m i c h a e l

#26
This should make you a mixin expert, Cormullion! (said with tongue firmly in cheek)



http://www.neglook.com/images/mixins.png">



If you think of mixins as rubber stamps, it's like we're stamping simpler contexts onto progressively more complex ones. Notice that we only define the first five. The rest are mixed in (using new) from these initial five contexts. Now imagine if, instead of the letters A through E, each context contained a number of functions and symbols. You can see that a very complex context can be made from a few simpler ones. Also, any changes made to C1 are reflected in C6 through C14 (the DRY* principle).



m i c h a e l



* Don't Repeat Yourself

cormullion

#27
Quote from: "m i c h a e l"Here's a complex example ;-)


I'm starting to get it!



As any bricoleur knows, the best way to serve complex numbers is with a bit of almond bread:


(define (draw)
  (for (y -1 1.1 0.07)
      (for (x -2 1 0.03)
         (set 'z (complex x y) 'c 126 'a z )
         (while (and (< (abs (:rad (set 'z (:add (:mul z z) a)))) 2) (> (dec 'c) 32))
           )
         (print (char c))
      )
  (println)))

(draw)

~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||{{{{zzywqvwumz{|||||||}}}}}}}}~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||{{{{zyyxwuftwxyz{{||||||||}}}}}}}~~~~~~~~~~~~~~~
~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||{{{{{zzvtmspT Nsuuxz{{{{|||||||}}}}}}}}~~~~~~~~~~~~
~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||{{{zzzzzyxwtl      Itwyzz{{{{{{||||}}}}}}}}}~~~~~~~~~~
~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||{{{zyyyyyyyyxwvt       ftwxxyzzzzzzz{{|||}}}}}}}}}~~~~~~~~
~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}||||||||{{{{{zzywpfptvvrYll^G B     cLjdsdnwxxxxury{{||}}}}}}}}}~~~~~~~
~~~~~}}}}}}}}}}}}}}}}}}}}}}|||||{{{{{{{{{zzzyywul   h                   dasY#genvz{{||}}}}}}}}}}~~~~~
~~~~}}}}}}}}}}}}}}}}}}}||||{{{{{{{{{{{{zzzzyxvvtpd                             vyz{{|||}}}}}}}}}}~~~~
~~~}}}}}}}}}}}}}}}||||{zyzzzzzz{{{zzzzzyyyxvK                                 qwxyz{{|||}}}}}}}}}}~~~
~~}}}}}}}}}||||||||{{{zzwsxxyyyxvuxyyyyyxxwkm                                  /Sqwz{|||}}}}}}}}}}}~~
~~}}}}||||||||||{{{{{zzyxvtbNqttriSspvwwwvtf                                    qwyz{||||}}}}}}}}}}~~
~}}}||||||||||{{{{{{zyyyxwto         SP`sso                                      ey{{||||}}}}}}}}}}}~
~}|||||||||{{{{{{{zwxxwwuo              Yn^                                     sxz{{|||||}}}}}}}}}}~
~||||||||{zzzzzyyyxwujqopT                                                      xz{{{|||||}}}}}}}}}}~
~{{yzzxvevwxvvwwvsssf                                                        swxyz{{{|||||}}}}}}}}}}~
~{{{zzzywyyyzyxxxuvtrk   T                                                    8xyz{{{|||||}}}}}}}}}}~
~|||||||||{{zzzzzyyxWuuts^               e                                     Hvyz{{|||||}}}}}}}}}}~
~}}|||||||||{{{{{{{zyyyxespK            oqV                                     qwz{{||||}}}}}}}}}}}~
~}}}}||||||||||{{{{{{zyyxwumJ k.Q Y /oquutre                                    swyz{||||}}}}}}}}}}}~
~~}}}}}}|||||||||{{{{zzywmttsvwvdevvuwxwwwup                                     nmz{||||}}}}}}}}}}~~
~~~}}}}}}}}}}}||||||{{zzwtxyyyyyyyyyyyyyyxxvslH                                twwvz{|||}}}}}}}}}}~~~
~~~~}}}}}}}}}}}}}}}}}|||{{{{{{{{{{{{{zzzzyyskhem                             puwyzz{{||}}}}}}}}}}~~~~
~~~~~}}}}}}}}}}}}}}}}}}}}||||{{{{{{{{{{{zzzzyxwtm                         f   iuxz{{||}}}}}}}}}}~~~~~
~~~~~~}}}}}}}}}}}}}}}}}}}}}}|||||||{{{{{{{zzzykpG _lrX                 quvsgtsrsz{||}}}}}}}}}}~~~~~~
~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}||||||||||{{{zy[vmpvwwwtsutspS     pstvvjwyyyyyxtz{||}}}}}}}}}}~~~~~~~
~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||{{{zzzzzyyyxwp       Dnwxyzzzz{{{{{|||}}}}}}}}}~~~~~~~~~
~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||{{{zzzyxwtJ&    domuyz{{{{{||||||}}}}}}}}~~~~~~~~~~~
~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||{{{{{zyxwvpG ntxxz{{{{|||||||}}}}}}}}~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||{{{{{zyxwumqvxzz{||||||||}}}}}}}~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||{{{{zypuxyywz|||||||}}}}}}}~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||{{ymwy{{{||||||}}}}}}}~~~~~~~~~~~~~~~~~~~~~~

cormullion

#28
(and I'm studying the rest of your posts...!)

newdep

#29
(while (michael does coding)

 (apply (cormullion (dansfloor $idx))

  (catch (lutz) newrelease)

   (unify 'Fanda 'OO)

    (silent newdep))





Fantastic example of objects Michael, I like it..so does the gui display ;-)



...A very nice Bread out of the oven Cormullion.. ;-)





realy great examples...
-- (define? (Cornflakes))