implementing delegation

Started by BrickCaster, June 29, 2004, 12:32:44 PM

Previous topic - Next topic

BrickCaster

this is a quick and dirty example of delegation prototype programming in newLISP:



(define-macro (handler)

  (define (self msg a b)

    (define slot (eval msg))

    (if (lambda? slot)

      (eval (list slot a b))

      slot

    )

  )

)



(define (interval x1 x2)

  (define xmin x1)

  (define xmax x2)

  (define (glide dx)

    (inc 'xmin dx)

    (inc 'xmax dx)

  )

  (define (rectangle y1 y2)

    (define ymin y1)

    (define ymax y2)

    (define (move dx dy)

      (self 'glide dx)

      (inc 'ymin dy)

      (inc 'ymax dy)

    )

    (handler)

  )

  (handler)

)



(set 'int (interval 0 639))

(set 'rect (int 'rectangle 0 479))

(rect 'move 20 20)

Lutz

#1
Hi Damien, welcome to the group and many thanks for your positive words about newLISP.



The formatting of your delegation example got lost in the post, so I am posting it again with code tags.



(define-macro (handler)
(define (self msg a b)
(define slot (eval msg))
(if (lambda? slot)
(eval (list slot a b))
slot)))

(define (interval x1 x2)
(define xmin x1)
(define xmax x2)

(define (glide dx)
(inc 'xmin dx)
(inc 'xmax dx))

(define (rectangle y1 y2)
(define ymin y1)
(define ymax y2)

(define (move dx dy)
(self 'glide dx)
(inc 'ymin dy)
(inc 'ymax dy))
(handler))
(handler))

(set 'int (interval 0 639))
(set 'rect (int 'rectangle 0 479))
(rect 'move 20 20)


Lutz

BrickCaster

#2
thanks for your attention.



my understanding of newLISP OOP model is it's much more practical than i thought at first. it even offers multiple inheritance with no hassle.



finally i even have removed my OCAML installation, i can't even read the OCAML documentation, CAML is really nice but in my opinion they have messed things up with the OOP extension. i wish it would have been as neat and simple as newLISP is.  



however when i practice delegation with newLISP (i am like a girl, i want everything) the concepts are all here, yet i face some limitations, well illustrated by the example:



1. (define (self msg a b) means i can't have more than 2 method args

2. incrementality requires a define-swith similar to the context-switch

3. even worse: it's not possible to distinct between a method and an object because both are lambdas



Note the 2 first limitations do not exist in Scheme, because:



1. (lambda (msg . args) allows to access the argument list

2. (eval expr env) allows to switch the evaluation environment



May be i am not adventurous enough, and there is a way to achieve the same effect in newLISP, but i still have not found how (i really bless you because the newLISP doc is so short!).



Have you some hack i have not seen?

It would be really great if delegation programming could be a practical alternative, because delegation programming offers the sharing possibilities that the built-in OOP model misses.

Delegation programming should be more than a toy.

Ideally newLISP should be practical whatever the preferred style.

That's how i see it: newLISP is beyond programming style.



regards,



- damien



(i know i am too severe, newLISP is great)

eddier

#3
I don't use OOP but there is a way to evaluate more then 2 args in a function.



Use:

(define-macro (whatever)

and then work on the

(args)

list.



For example here is my gcd (greatest common divisor) function that operates on multiple arguments.



(define (gcd_ a b)
  (let (r (% b a))
    (if (= r 0)  a  (gcd_ r a))))

(define-macro (gcd )
  (apply gcd_ (map eval (args)) 2))


See the documentation on apply. Use 2 if f has 2 parameters or 3 if f has 3 parameters, etc...



In this case (gcd 4 12 6 24)) ==

(gcd_ (gcd_ (gcd_ 4 12) 6) 24) => 2

Hope this helps.



Eddie

BrickCaster

#4
thanks eddier.



the new code may be less efficient, yet it removes the arguments problem.

that also partially solves the "identity" problem, because objects are macros whereas methods are lambdas.

(define (handler)
  (define-macro (self msg)
    (define slot (eval (eval msg)))
    (if (lambda? slot)
      (eval (cons slot (map eval (rest (args)))))
      slot
    )
  )
)

(define (interval x1 x2)
  (define xmin x1)
  (define xmax x2)
  (define (glide dx)
    (inc 'xmin dx)
    (inc 'xmax dx)
  )
  (define (rectangle y1 y2)
    (define ymin y1)
    (define ymax y2)
    (define (move dx dy)
      (self 'glide dx)
      (inc 'ymin dy)
      (inc 'ymax dy)
    )
    (handler)
  )
  (handler)
)

(set 'int (interval 0 639))
(set 'rect (int 'rectangle 0 479))
(rect 'move 20 20)

Lutz

#5
But note that lambda and lambda-macro expressions in newLISP don't do a lexical closure, so multiple rectangle or interval objects would share the same xmin, ymin and xmax, ymax.



If I understand delegation correctly it relies on the lexical closure mechanism of lambda expressions in other LISPs ?



That is why in newLISP contexts are probably the better mechanism to do OO.





(context 'rectangle)

(define (init  x1 x2 y1 y2)
(set 'xmin x1 'xmax x2 'ymin y1 'ymax y2 ))

(define (glide dx)
(inc 'xmin dx)
(inx 'xmax dx))

(define (move dx dy)
(glide dx)
(inc 'xmin dy)
(inc 'ymax dy))

(context 'MAIN)

(new rectangle 'rect)
(rect:init 0 639 0 479)
(rect:move 20 20)

(new rectangle 'rect2)
...
;; etc




Lutz

BrickCaster

#6
that's indeed how delegation is implemented in LISP languages: an object is a lexical closure that exports its bindings.

nested lexical closures provide the inheritance effect.



unfortunately, without lexical closures newLISP is not suitable for implementation of new OOP systems. so newLISP is only a language, not the meta-language i expected it to be.



i may still have a usage for newLISP (LEGO models scripting), but i have not decided yet (i have to investigate whether i can live without closures).

Lutz

#7
contexts are lexically closed and can be created (or destroyed) dynamically and referred to by variable (see manual), so you could create an object closure and treat it as a parameter:



(context 'interval)

(define (glide dx)
    (inc 'xmin dx)
    (inc 'xmax dx))

(define (make ctx x1 x2)
  (new interval ctx)      ;; context created during runtime
  (set 'ctx (eval ctx))   ;; and referred to by variable
  (set 'ctx:xmin x1)
  (set 'ctx:xmax x2)
  ctx)

(context 'MAIN)

(interval:make 'foo 0 639) => foo

foo:xmax => 639
(foo:glide 20) => 659

(set 'var foo)
var:xmax => 659   ;; context foo referenced by var

;; etc


Lutz