My continuing adventures in FOOP have led me to a new discovery: mutable FOOP objects.
By adding a self-reference to its second element, a mutable object can preserve its state without manually resetting the reference each time the object is updated. (Actually, manual resetting still occurs, but it's hidden within the object's methods.)
Preliminary disclaimer: Please remember this code is still experimental and unproven. Proceed with care. You can view (and the brave can download) the code here.
First, let me introduce a class method and three object methods that can be included in your "init.lsp" file in case you do a lot of FOOP programming (or you could put it in a file called "foop.lsp" that you load before doing serious FOOP):
Code: Select all
;; [class method] generic class predicate
(define (Class:? it) (= (and it (list? it) (it 0)) (context)))
;; making objects displayable
(define (Class:string it) (MAIN:string it))
(define (Class:print it) (MAIN:print (:string it)))
(define (Class:println it) (:print it) (MAIN:print "\n"))
I've had these methods defined in my "init.lsp" for a long time now without any problems, but everyone's setup is different, so beware!
Next is the code for doing mutable objects:
Code: Select all
(new Class 'Mutable)
;; note: mutable objects require accessors to update their state
;; an inherited helper method called :access simplifies accessor writing
;; the constructor creates the object's reference
(define (Mutable:Mutable id)
(set id (cons (context) (cons id (args))))
)
;; an accessor for the object's id (currently read-only)
(define (Mutable:id it) (it 1))
;; a helper method for writing accessors
(define (Mutable:access it idx value allow-nil?)
(setq this (eval (:id it)))
(if (or value allow-nil?)
(begin
(setf (this idx) value)
(set (:id it) this)
)
(this idx)
)
)
Code: Select all
(new Mutable 'Point)
;; keeping a reference to the inherited constructor so we can overwrite it
(setq Point:^Point Point:Point)
;; overwriting the constructor
(define (Point:Point id (x 0) (y 0))
(Point:^Point id x y)
)
;; Point's x and y accessors using inherited :access method
(define (Point:x it value) (:access it 2 value))
(define (Point:y it value) (:access it 3 value))
;; Point's string representation
(define (Point:string it) (replace "MAIN:" (string it) ""))
;; moving a Point to a specific place (x y)
(define (Point:move it x y) (:x it x) (:y it y))
;; sliding a Point by some amount (x y)
(define (Point:slide it x y)
(:move it (MAIN:+ (:x it) x) (MAIN:+ (:y it) y))
)
;; adding two Points together
(define (Point:+ it other)
(:slide it (:x other) (:y other))
)
Code: Select all
;; a helper function that displays an expression and its result
;; as if it had been entered on the command-line
(define (run e)
(print "> " (string e) "\n")
(:println (eval e))
)
(println "Sample Run using Mutable Points")
(run '(Point 'p1 10 20))
(run 'p1)
(run '(:slide p1 5 8))
(run 'p1)
(run '(Point 'p2 88 99))
(run '(:+ p1 p2))
(run 'p1)
(run 'p2)
(println "> _")
Code: Select all
Sample Run using Mutable Points
> (Point 'p1 10 20)
(Point p1 10 20)
> p1
(Point p1 10 20)
> (: slide p1 5 8)
(Point p1 15 28)
> p1
(Point p1 15 28)
> (Point 'p2 88 99)
(Point p2 88 99)
> (: + p1 p2)
(Point p1 103 127)
> p1
(Point p1 103 127)
> p2
(Point p2 88 99)
> _
That's it! Mutable objects. Looking forward to hearing how they work for you!
m i c h a e l