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:
Code: Select all
> (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:
Code: Select all
(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:
Code: Select all
> (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:
Code: Select all
(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:
Code: Select all
> (data-type (pixel x y color))
pixel
> (eval (eval (eval (pixel 45 23 "blue"))))
(pixel 45 23 "blue")
> _