newLISP Fan Club

Forum => Anything else we might add? => Topic started by: m i c h a e l on October 06, 2007, 10:58:28 AM

Title: More about Lutz's new macro
Post by: m i c h a e l on October 06, 2007, 10:58:28 AM
I decided to dig up the link (//http) to a site that contains a number of object-oriented implementations of the classic shape example used in most OOP books. Armed with only Lutz's new macro, I set out to see how far I could get. I used the Ruby implementation as a starting point. Here's shape.lsp (note: I'm using data-type instead of def-class in the following code :-):


(load "/lisp/data-type.lsp")

(data-type (shape x y))

(define (shape:move shp newx newy)
(shape:x shp newx)
(shape:y shp newy)
)

(define (shape:r-move shp newx newy)
(shape:move shp (+ (shape:x shp) newx) (+ (shape:y) newy))
)


If you compare this code with the Ruby version, you'll see it's quite a bit shorter. And if you run it, you'll also see that it doesn't work!



Here is how I had to rewrite shape:move for it to function properly:


(define-macro (shape:move shp newx newy)
(nth-set ((eval shp) 0) (eval newx))
(nth-set ((eval shp) 1) (eval newy))
)


It works, but it also seems too low-level. I'm not even sure how to change shape:r-move to make it work correctly. Am I overlooking something? (Most probable.) Should a companion macro be introduced to create methods for these new data types? Will Lutz begin hating me for my unhealthy fixation on OO?! ;-)



m i c h a e l
Title:
Post by: rickyboy on October 06, 2007, 08:57:23 PM
Oh boy!  It seems as if we can't get around pass-by-value unless our data is in a context (and we wanted to try to avoid that in the first place! :-).  However, one way to get around it is to rewrite data-type as
(define-macro (data-type)
  (let (ctx (context (args 0 0)))
    (set (default ctx) (lambda () (args)))
    (dolist (item (rest (args 0)))
      (set (sym item ctx)
           (expand '(lambda (_str _val)
                      (if _val
                          (nth-set ((eval _str) $idx) _val)  
                          ((eval _str) $idx))) '$idx)))
    ctx))

Notice that the setters and getters are NOT macros now, but regular functions.  But now your calling code should look like this:
> (setq s1 (shape 4 2))
(4 2)
> s1
(4 2)
> (shape:move 's1 5 6)
2
> s1
(5 6)
> (shape:r-move 's1 2 3)
6
> s1
(7 9)

Notice now that all the functions are passing around the *symbol* s1 until it floats down to the setter/getter which resolves the symbol to the actual object -- kind of a poor man's pass-by-reference.  The good news is that definition of your move functions remain the same.



Hope that helps.  --Ricky



P.S. -- BTW I took the liberty of fixing a typo in the definition of r-move: I had to change the expression (shape:y) to (shape:y shp).
Title:
Post by: rickyboy on October 06, 2007, 09:40:51 PM
Well, what I just said in the previous post still stands, HOWEVER ... a potential problem still remains: we still can't deal in anonymous "objects".



Recall m i c h a e l's example:
> (data-type (person last first))
person
> (data-type (book title author isbn))
book
> (set 'catch-22 (book "Catch-22" (person "Heller" "Joseph") "0-684-83339-5"))
("Catch-22" ("Heller" "Joseph") "0-684-83339-5")
> (person:first (book:author 'catch-22))

value expected in function eval : "Joseph"
called from user defined function person:first

Oops!  What happened?  Well what happened is that the inside expression is resolving to ("Heller" "Joseph"), and so person:first is now trying to deal with an anonymous object.  We could make yet another change to data-type which could fix this situation:
(define-macro (data-type)
  (let (ctx (context (args 0 0)))
    (set (default ctx) (lambda () (args)))
    (dolist (item (rest (args 0)))
      (set (sym item ctx)
           (expand '(lambda (_str _val)
                      (let ((_str (if (symbol? _str)
                                      (eval _str) _str)))
                        (if _val
                            (nth-set (_str $idx) _val)  
                            (_str $idx)))) '$idx)))
    ctx))

This has the getter/setter check if the object passed to it is anonymous or not.  It works for this situation:
> (person:first (book:author 'catch-22))
"Joseph"

But if I tried to use the setter to change the author's first name, it doesn't work:
> (person:first (book:author 'catch-22) "Dweezil")
"Joseph"
> (person:first (book:author 'catch-22))
"Joseph"

Now we are back to the original problem: the setter person:first is working with a COPY of (book:author 'catch-22), not the original object.



We have to think of a better implementation.  Hmm, interesting puzzle ...
Title:
Post by: Lutz on October 07, 2007, 12:18:13 AM
QuoteNotice that the setters and getters are NOT macros now, but regular function


They are macros, the 'lambda' in my post was a temporary change, which has been taken back to 'lambda-macro'. Here again the correct implementation (now called def-type ;-) ):


(define-macro (def-type)
  (let (ctx (context (args 0 0)))
    (set (default ctx) (lambda () (args)))
    (dolist (item (rest (args 0)))
      (set (sym item ctx)
      (expand '(lambda-macro (_str _val)
          (if (set '_val (eval _val))
          (nth-set ((eval _str) $idx) _val)    
          ((eval _str) $idx))) '$idx)))
ctx))


The macro is necessary for the setter part, so the symbol can be accessed without quoting it (as in Rickyboy's workaround in the previous post). The only way around this is using contexts, which can be used to pass wrapped data by reference.



But the 'anonymous' object question still remains. In newLISP each object you want to access must be somehow anchored in a symbol. This is why the original object implementation in the manual works with contexts as objects, because you can pass them to a function without a macro:


(set 'person1:name "joseph")
(set 'person1:address "San Francisco")

(define (change-name p n)
(set 'p:name n))

(change-name person1 "Dweezil")

person1:name => "Dweezil"


The original template object implementation with contexts works pretty well except for two details: (1) each object carries along with it is a full copy of all the methods, and (2) no anonymous objects, each object must be attached to a context/symbol.



Again newLISP is not designed to be an OO language. A 'def-type' like macro works well for structuring data and packageing all functions related to that data type into one context. But its not original OO where data and methods are together in one object.



Lutz
Title:
Post by: m i c h a e l on October 07, 2007, 04:22:07 AM
Note: I wrote a response to Ricky's first response before I saw his second response or even Lutz's response to his responses (did you follow that?). I'll post my original response following this one.



Lutz,



You have often said (and you must be getting well sick of it by now ;-) that newLISP is not designed to be an OO language. Fair enough. But what about those of us who have already had the OO way embedded into our brains? If it is possible, why discourage us from writing our newLISP programs in a way that is natural to us? Aren't you gently forcing us to convert to FP or be turned away in frustration?



I myself have earnestly tried to embrace the functional way of programming. But long-developed habits die hard (and to be honest, I'm not sure I want to lose them) and, as for me, once a functional program grows to a certain size, I can no longer understand how it works (FontComp is beginning to make my head swim). I concede that this difficulty may stem from my ignorance of maths and proper programming practices. Maybe that's why object-oriented programming has always appealed to me. It's fun to build things out of Legos!



I guess what I'm trying to say is: Please Lutz, have a little sympathy for us poor object-heads :-) If a module filled with a couple of macros and functions can fake it well enough to fool us into believing newLISP is the greatest OOP language invented, then why not?



Besides, we wouldn't be using OOP anyway. We would be FOOPing. Functional object-oriented programming ;-)



m i c h a e l
Title:
Post by: m i c h a e l on October 07, 2007, 04:22:25 AM
Note: This is the original response promised in the above post. This code will no longer work with Lutz's redefined macro def-type :-(



Hi Ricky!



Thank you for taking the time to solve this. With your solution, I was able to finish the implementation with only one small change.



The polymorphism test required shapes in a list to respond in a polymorphic way to the same messages (draw and r-move-to). Because our methods are separated into different contexts (rightly so), how could one do this? My solution was to modify data-type to include the context as the first element of the object's list representation. This also required the index for the generated accessors to be one greater than before. Now we can get the object's type by retrieving its first element:


> (data-type (point x y))
point
> (set 'pt (point 11 33) 'type (pt 0))
point
> (type:y 'pt)
33
> _


All in all, this makes for a beautifully simple and clean expression of the OO shape example:


(load "/lisp/data-type.lsp")

; shape

(data-type (shape x y))

(define (shape:move-to shp newx newy)
(shape:x shp newx)
(shape:y shp newy)
)

(define (shape:r-move-to shp newx newy)
(shape:move-to shp
(+ (shape:x shp) newx)
(+ (shape:y shp) newy)
)
)

; rectangle

(data-type (rectangle x y width height))

(new shape rectangle)

(define (rectangle:draw rec)
(println
"Draw a rectangle at:(" (rectangle:x rec) "," (rectangle:y rec)
"), width " (rectangle:width rec)
", height " (rectangle:height rec)
)
)

; circle

(data-type (circle x y radius))

(new shape circle)

(define (circle:draw cir)
(println
"Draw a circle at:(" (circle:x cir) "," (circle:y cir)
"), radius " (circle:radius cir)
)
)

; polymorphism test

; create a collection containing various shape instances
(set 'scribble
(list
(rectangle 10 20 5 6)
(circle 15 25 8)
)
)

; iterate through the collection and handle shapes polymorphically
(dolist (ea scribble)
(set 'type (ea 0))
(type:draw 'ea)
(type:r-move-to 'ea 100 100)
(type:draw 'ea)
)

; access a rectangle specific function
(set 'a-rectangle (rectangle 0 0 15 15))
(rectangle:width 'a-rectangle 30)
(rectangle:draw 'a-rectangle)


The newLISP version is about half the size of the Ruby version. Of course, this is because our macro is writing the constructor and accessors for us, but still, even at this early stage, the new macro feels comfortable to use and fits newLISP well.



With the addition of the context in the type's representation, the macro could now also create functions for testing an object's type:


> (data-type (complex real imaginary))
complex
> (set 'cn (complex 3 2))
(complex 3 2)
> (complex:? 'cn)
true
>


What else could the macro write for us?



Here is the updated macro. I'm not sure my solution is that great, so any suggested changes are welcome:


(define-macro (data-type)
  (let (ctx (context (args 0 0)))
    (set (default ctx)
(expand
'(lambda () (append (list ctx) (args)))
'ctx
)
)
    (dolist (item (rest (args 0)))
      (set
'idx (+ $idx 1)
(sym item ctx)
       (expand
'(lambda (_str _val)
          (if _val
            (nth-set ((eval _str) idx) _val)    
             ((eval _str) idx)
)
)
'idx
)
)
)
    ctx
)
)


m i c h a e l



P.S. Good catch on the missing argument to shape:y. Thanks!



P.P.S. An interesting thing about this new representation: it mirrors the expression that created it, so we can evaluate the result and keep getting the same result:



> (data-type (pixel x y color))
pixel
> (eval (eval (eval (pixel 45 23 "blue"))))
(pixel 45 23 "blue")
> _
Title:
Post by: newBert on October 07, 2007, 05:07:42 AM
Quote from: "Lutz"


Again newLISP is not designed to be an OO language. A 'def-type' like macro works well for structuring data and packageing all functions related to that data type into one context. But its not original OO where data and methods are together in one object.



Lutz


Fortunately ! OOP is not panacea and is a little heavy by comparison with "functional" solutions.



For lighter OOP 'contexts' are maybe THE solution ... see what is NOOP (Natural OOP) with ELICA (a Lisp-descendant language since it's a Logo-like language) :

//http://www.elica.net/download/papers/ElicaLogoObjects.pdf

//http://www.elica.net/download/papers/NOOP-TheEvolutionMetaphorInElica.pdf



Sorry if my message is inopportune ( I don't understand in detail everything you wrote ... as a non-english speaking guy ;) )



long live newLisp !

:)
Title:
Post by: cormullion on October 07, 2007, 09:31:21 AM
I'm enjoying these OO posts, although I can't contribute much, apart from the occasional murmur of appreciation.
Title:
Post by: Lutz on October 08, 2007, 12:45:05 AM
Quote from: "m i c h a e l"This is the original response promised in the above post. This code will no longer work with Lutz's redefined macro def-type :-(


Should work by just passing the object symbol unquoted?


Quote from: "m i c h a e l"My solution was to modify data-type to include the context as the first element of the object's list representation.


This idea is crucial. Now we have the data linked to its interface without copying in each new object. Here a small improvement in the constructor template:


; instead of
(expand '(lambda () (append (list ctx) (args))) 'ctx)
; use
(expand '(lambda () (cons ctx (args))) 'ctx)


'cons' is rarely used in newLISP, but this is the place ;)



Lutz
Title:
Post by: rickyboy on October 08, 2007, 02:32:23 AM
Quote from: "m i c h a e l"Thank you for taking the time to solve this. With your solution, I was able to finish the implementation with only one small change.

Small yes, but also significant and smart.  Sure it doesn't look like much on the surface, but that would belie the work that it took to figure out the right change to make. What did you do, like change *two* lines of code, and boom, you have polymorphism?  Awesome.  I rarely ever get that kind of ROI. :-)  Nice work, m i c h a e l!
Title:
Post by: m i c h a e l on October 08, 2007, 05:30:56 AM
Quote from: "cormullion self-deprecatingly"although I can't contribute much, apart from the occasional murmur of appreciation.


Are you kidding? You can give us a perspective I myself lost long ago. I'm too marinated in the object-think soup, now. Sometimes when you get too familiar with a subject, someone simply paying attention can see what you have become blind to.



m i c h a e l
Title:
Post by: m i c h a e l on October 08, 2007, 05:31:23 AM
Quote from: "Lutz"Should work by just passing the object symbol unquoted?


Unfortunately, we still have the problem mentioned by Ricky. It doesn't work with the following example run from within newLISP edit:


(load "/lisp/def-type.lsp")

(def-type (shape x y))

(set 's1 (shape 0 0))

(shape:x s1  25)
(shape:y s1 624)

s1

(define (shape:move-to shp newx newy)
(shape:x shp newx)
(shape:y shp newy)
)

(shape:move-to s1)

s1

=============================o=============================

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

> (lambda-macro ()
 (let (ctx (context (args 0 0)))
  (set (default ctx) (expand '(lambda () (cons ctx (args))) 'ctx))
  (dolist (item (rest (args 0)))
   (set 'idx (+ $idx 1) (sym item ctx) (expand '(lambda-macro (_str
       _val)
      (if (set '_val (eval _val))
       (nth-set ((eval _str) idx) _val)
       ((eval _str) idx))) 'idx))) ctx))
shape
(shape 0 0)
0
0
(shape 25 624)
(lambda (shp newx newy) (shape:x shp newx) (shape:y shp newy))
624
(shape 25 624)
>


As you can see, s1 is still (shape 25 624) instead of the correct (shape 50 1248).



If macros are necessary for our data type functions to operate correctly, maybe we need a function (or macro) to write them for us (like def-type is doing).


Quote from: "Lutz"'cons' is rarely used in newLISP, but this is the place ;)


Thanks, Lutz. I knew that code of mine smelled, but like you said, I don't think I've used the cons function even once.



m i c h a e l
Title:
Post by: m i c h a e l on October 08, 2007, 05:31:41 AM
Ricky,



Thank you for you kind words. Even though I've been working with programming languages all these many years, I'm still an embarrassingly poor programmer. Your encouragement gives me hope that all this time hasn't been spent in vain :-)



m i c h a e l
Title:
Post by: Lutz on October 08, 2007, 09:08:36 AM
Quote from: "m i c h a e l"If macros are necessary for our data type functions to operate correctly, ...


Either write all data modifying accessor functions using lambda-macro and not have to quote symbols or write all desctrucive accessors as normal lambda funtions and quote the object symbol when required. Code is easier to read and slightly faster when using the norma define/lambda and quote the symbols if necessary ... decisions, decisions ... ;-)



Perhaps writing a bigger app gives an answer what is more convenient in the long run.



Lutz
Title:
Post by: m i c h a e l on October 08, 2007, 10:23:46 AM
Quote from: "Lutz"Either write all data modifying accessor functions using lambda-macro and not have to quote symbols or write all desctrucive accessors as normal lambda funtions and quote the object symbol when required. Code is easier to read and slightly faster when using the norma define/lambda and quote the symbols if necessary ... decisions, decisions ... ;-)


Using Ricky's modification (where quoting was necessary) still felt perfectly natural and not at all obtrusive. Best to keep it as close to the way newLISP already does things.


Quote from: "Lutz"Perhaps writing a bigger app gives an answer what is more convenient in the long run.


That is a good idea. OO was supposed to be the silver bullet against complexity*, so many problems wouldn't crop up until a program obtains a certain mass to it. Any idea what program it should be?



m i c h a e l



* We know how that turned out ;-)
Title:
Post by: newdep on October 10, 2007, 01:08:04 PM
I hope this topic is not closed yet because it intresting ;-)

Especialy the addon from newBert, I liked the "light" intepretation.




QuoteAgain newLISP is not designed to be an OO language. A 'def-type' like macro works well for structuring data and packageing all functions related to that data type into one context. But its not original OO where data and methods are together in one object.


Will there be perhpas an internal change-addon to newlisp? Who knows.. ;-)
Title:
Post by: m i c h a e l on October 15, 2007, 10:13:49 AM
I'm continuing to work through possible ways to model OO in newLISP. The PDFs on the Logo-based Elica that newBert posted links for (thanks, newBert!) have been fascinating. What Pavel describes in the papers is strikingly like newLISP's contexts. But once you begin to work with contexts as objects, the problem of not being able to nest contexts rears its ugly head. Or does it?



Since all newLISP contexts must be named with a symbol in the MAIN context only, I wonder if it would be helpful to think of objects in newLISP not like this:



(//%3C/s%3E%3CURL%20url=%22http://www.kenrockwell.com/bmw/images/m3-2007/top-790.jpg%22%3E%3CLINK_TEXT%20text=%22http://www.kenrockwell.com/bmw/images/m%20...%20op-790.jpg%22%3Ehttp://www.kenrockwell.com/bmw/images/m3-2007/top-790.jpg%3C/LINK_TEXT%3E%3C/URL%3E%3Ce%3E)



But instead, like this:



(//%3C/s%3E%3CURL%20url=%22http://www.kenrockwell.com/bmw/images/m3-2007/exploded-789.jpg%22%3E%3CLINK_TEXT%20text=%22http://www.kenrockwell.com/bmw/images/m%20...%20ed-789.jpg%22%3Ehttp://www.kenrockwell.com/bmw/images/m3-2007/exploded-789.jpg%3C/LINK_TEXT%3E%3C/URL%3E%3Ce%3E)



Something to think about?



I've written some code that begins to duplicate the examples in Pavel's "Elica Logo and Objects (//http)" paper. I wound up with something similar to the way m35's .new function (link (//http)) works, but by using a mixin called Class.


; C L A S S E S

(context 'Class)

(set '_next -1 'type Class)

(define (next) (sym (string (context) "-" (inc '_next)) MAIN))

(define (Class:new) (set 'obj (next)) (MAIN:new Class:type obj) (eval obj))

(define (Class:? obj) (= obj:type Class:type))

(context MAIN)


; P O I N T S

(set 'POINT:type POINT 'POINT:x 0 'POINT:y 0)

(new Class 'Point)

(set 'Point:type POINT)

(define (point x y) (set '_p (Point:new) '_p:x x '_p:y y '_p:self _p))

(set 'Point:zero (point 0 0))


; S E G M E N T S

(set 'SEGMENT:type SEGMENT 'SEGMENT:a Point:zero 'SEGMENT:b Point:zero)

(context 'Segment)

(set 'type SEGMENT)

(new Class)

(define (Segment:delete seg)
  (set 'pa seg:a 'pb seg:b)
  (and (MAIN:delete pa:self) (MAIN:delete pb:self) (MAIN:delete seg:self))
)

(context MAIN)

(define (segment ax ay bx by)
  (set '_s (Segment:new) '_s:a (point ax ay) '_s:b (point bx by) '_s:self _s)
)

(set 'Segment:zero (segment Point:zero Point:zero))


; T R I A N G L E S

(set
  'TRIANGLE:type TRIANGLE
  'TRIANGLE:ab Segment:zero
  'TRIANGLE:bc Segment:zero
  'TRIANGLE:ca Segment:zero
)

(context 'Triangle)

(set 'type TRIANGLE)

(new Class)

(define (Triangle:delete tri)
  (set 'ab tri:ab 'bc tri:bc 'ca tri:ca)
  (and
    (Segment:delete ab:self) (Segment:delete bc:self) (Segment:delete ca:self)
    (MAIN:delete tri:self)
  )
)

(context MAIN)

(define (triangle ax ay bx by cx cy)
  (set
    '_t (Triangle:new)
    '_t:ab (segment ax ay bx by)
    '_t:bc (segment bx by cx cy)
    '_t:ca (segment cx cy ax ay)
    '_t:self _t
  )
)

(set 'Triangle:zero (segment Point:zero Point:zero))

; S A M P L E R U N
; NOTE: The following code makes use of a user-defined function named 'bug'.
; 'bug' is not part of newLISP.

(bug (set 'pt (point 54 22)))
(bug pt:x pt:y pt:self (Point:? pt))

(bug (set 'sg (segment 23 54 65 34)))
(bug sg:a sg:b sg:self (Segment:? sg))

(bug (set 'pa sg:a))
(bug pa:x pa:y pa:self)

(bug (set 'pb sg:b))
(bug pb:x pb:y pb:self)

(bug (Segment:delete sg))
(bug sg:self pa:self pb:self)

(bug (set 'tr (triangle 11 22 45 23 65 34)))
(bug tr:ab tr:bc tr:ca (Triangle:? tr))

(bug (Triangle:delete tr))
(bug tr:self tr:ab tr:bc tr:ca)


This produces the following output:


-> (set 'pt (point 54 22)) = Point-5
-> pt:x = 54, pt:y = 22, pt:self = Point-5, (Point:? pt) = true
-> (set 'sg (segment 23 54 65 34)) = Segment-2
-> sg:a = Point-6, sg:b = Point-7, sg:self = Segment-2, (Segment:? sg) = true
-> (set 'pa sg:a) = Point-6
-> pa:x = 23, pa:y = 54, pa:self = Point-6
-> (set 'pb sg:b) = Point-7
-> pb:x = 65, pb:y = 34, pb:self = Point-7
-> (Segment:delete sg) = true
-> sg:self = nil, pa:self = nil, pb:self = nil
-> (set 'tr (triangle 11 22 45 23 65 34)) = Triangle-0
-> tr:ab = Segment-3, tr:bc = Segment-4, tr:ca = Segment-5, (Triangle:? tr) = true
-> (Triangle:delete tr) = true
-> tr:self = nil, tr:ab = nil, tr:bc = nil, tr:ca = nil


I've separated the contexts used to represent the objects (all uppercase) from the ones used as instance makers and method holders (title-case). I forget why I thought this was a good idea ;-)



The next thing is to model the interesting concept of rules (from the same paper (//http)). That should be fun!



This is just one more experiment. One of many. But it solves the problem of making and deleting objects (contexts) without having to name them directly (semi-anonymous objects?). Any ideas for improvements or fixes?



m i c h a e l



P.S. If you're interested in the code for bug, let me know and I'll post it. But you have to promise to help bum it ;-)
Title:
Post by: m i c h a e l on October 15, 2007, 03:26:21 PM
P.P.S. Sorry for the grotesquely large images lately. I'm linking directly to the original sites, so I can't change the image size directly. Is there a way to adjust the size within the image tag? I've tried using (I've excluded the <>s in the following, so it will show up):



img src="http://www.kenrockwell.com/bmw/images/m3-2007/top-790-big.jpg" alt="Whole Engine" width="640" height="480"  /



but this produces only:



<img>



Thanks for any help you can offer.



m i c h a e l
Title: Re: More about Lutz's new macro
Post by: newBert on October 17, 2007, 01:58:12 AM
Quote from: "m i c h a e l"I decided to dig up the link (//http) to a site that contains a number of object-oriented implementations of the classic shape example used in most OOP books.




Here is an example of mine. It is very simple but it shows the use of 'inheritance' and 'polymorphism' ... if I'm not mistaken, I'm not a specialist of OOP (but maybe of OOPS !)


#!/usr/bin/newlisp
;================================================
; Class & Objects with CONTEXT
; ----- newlisp 9.2.3 --------- oct. 2007 -----
;================================================

(context 'rectangle)
; class rectangle
(define (rectangle:rectangle (_width 30) (_height 15))
(set 'width _width)
(set 'height _height)
(set 'shape "Rectangle"))

(define (perimeter)
(string "(" width " + " height ") x 2 = " (mul (add width height) 2)))

(define (area)
(string width " x " height " = " (mul width height)))

(define (measure)
(println shape " " width "X" height " :")
(println "- surface area = " (area))
(println "- perimeter = " (perimeter) "n"))

(context MAIN)

(context 'square)
; class square
(new rectangle)  ; square is a special rectangle
(define (square:square (side 10))
(set 'width side)
(set 'height side)
(set 'shape "Square"))

(context MAIN)

;; main program

(new rectangle 'f1)
(f1 27 12)
(f1:measure)

(new square 'f2)
(f2 13)
(f2:measure)
   
;; fin du script
Title:
Post by: itistoday on December 03, 2007, 05:24:40 PM
Hi, while playing around with Lutz's original macro I tried to do stuff like this:


(:x pt 2)

And it didn't work.  :p



So I set about to fix that.  I had planned on reading this thread when I was finished and low and behold I saw that michael had done a similar thing.  I then copied some of the suggestions made in this thread to make the code even nicer.  However, I'm still having problems, and forgive me but I only skimmed over this thread.



(BTW michael, here are smaller versions of those pictures, just remove the -big from the URL: pic1 (//http), pic2 (//http))



So here is my version as it is now:


(define-macro (data-type)
(let (ctx (context (args 0 0)))
    (set (default ctx) (expand '(fn () (cons ctx (args))) 'ctx))
    (dolist (item (rest (args 0)))
(set 'idx (+ $idx 1)
(sym item ctx)
(expand '(lambda-macro (_str _val)
(if (set '_val (eval _val))
          (nth-set ((eval _str) idx) _val)
          ((eval _str) idx)
)
)
'idx
)
)
)
ctx
)
)


However, (:x pt 5) still doesn't work!  It runs, it just doesn't modify pt. Oddly enough (point:x pt 5) does.  Can anyone help me out here as to why this is happening?



Also, how do I write a compound method like this:


(define-macro (point:move _pt _x _y)
(:x _pt _x)
(:y _pt _y)
)

; this doesn't work either

(define-macro (point:move _pt _x _y)
(point:x _pt _x)
(point:y _pt _y)
)

; if I try this:
(define-macro (point:move _pt _x _y)
(println (point:x _pt))
)

; I get this error:
invalid function : ((eval MAIN:_str) 1)


So that when I do (:move pt 5 6) [or (point:move pt 5 6)], point should contain the values 5 and 6.  Many thanks!
Title:
Post by: m i c h a e l on December 03, 2007, 05:53:58 PM
Quote from: "itistoday"(BTW michael, here are smaller versions of those pictures, just remove the -big from the URL: pic1, pic2)


Thank you! That looks much better.



m i c h a e l
Title:
Post by: itistoday on December 03, 2007, 06:19:58 PM
OK, I haven't been able to figure out how to get it working using the previous paradigm, but I did fix it by changing the semantics a bit.



The data-type function is the same except the setter/getters are functions instead of macros:


(define-macro (data-type)
(let (ctx (context (args 0 0)))
    (set (default ctx) (expand '(fn () (cons ctx (args))) 'ctx))
    (dolist (item (rest (args 0)))
(set 'idx (+ $idx 1)
(sym item ctx)
(expand '(lambda (_str _val)
(if (set '_val (eval _val))
          (nth-set ((eval _str) idx) _val)
          ((eval _str) idx)
)
)
'idx
)
)
)
ctx
)
)


Next, forget about the macro-like syntax of (point:x pt 5).  Instead, quote the point:


(point:x 'pt 5)

That works.  Now I've even been able to get compound functions (not macros) to work:


(define (point:move _pt _x _y)
(point:x _pt _x)
(point:y _pt _y)
(eval _pt)
)

(point:move 'pt 8 7)


That works too.  And not only that, but using the colon syntax for getting works too! (but *not* setting)

Except you use the previous format of not quoting the object.


(:x pt)

If anyone figures out how to get this working using macros though... let me know.  It seems to me that newLISP macros have something funky going on with them besides simply changing their parameters to be quoted...
Title:
Post by: itistoday on December 03, 2007, 06:29:14 PM
OK, this is getting frustrating, I'm starting to get the impression that newLISP just isn't designed for object oriented programming (edit: which appears to be what Lutz said above, now that I'm reading through this thread in more detail...).  Using my previous code this does not work:


(set 'points '())
(dotimes (i 5)
(push (point i i) points)
)
(println (point:move '(points 0) 200 200))


I'm guessing you've probably gotta jump through some hoops by using temporary variables to get that to work...  Yeah, this works:


(set 'points '())
(dotimes (i 5)
(push (point i i) points)
)
(set 'tmp (points 0))
(println (point:move 'tmp 200 200))
(nth-set (points 0) tmp)


This is slightly disappointing... I'm hoping some newLISP guru will put my newbie-self in place and show me the proper way to do this...



Edit:  I think it's possible to actually get this to work by writing code to anticipate this situation in data-type.  For example it could check (list? _str) and if so then do the temporary variable thing itself.  Anyone wanna give this a shot?  I won't be able to work on this right now because I've got an another project, but once that's done if no one's done it I'll give it a shot and post here if I'm successful.