;; @module ObjNL ;; @description Objective newLISP - Real Object Oriented Programming for newLISP ;; @version 1.0 ;; @author Greg Slepak ;; @location http://www.taoeffect.com/newlisp/ObjNL.lsp.txt ;;

Introductory Guide

;; The @link http://www.taoeffect.com/blog/2009/12/introducing-objective-newlisp/ official guide ;; is highly recommended reading if you are planning on using Objective newLISP. ;;

What is Objective newLISP?

;; Objective newLISP is a new and exciting way of doing real ;; object oriented programming in newLISP where instances are passed ;; by reference and can easily hold references to other objects while ;; maintaining their own mutable state. ;;

It supports most of the object oriented concepts you'll find ;; in other languages. It supports inheritance, interfaces (aka protocols), ;; as well as class and instance variables.

;;

Objects are passed by reference, so there's no problem with passing ;; an object through multiple user-defined functions and modifying it.

;;

Accessing instance variables no longer requires a function call plus a ;; list traversal. Simply access the symbol directly.

;;

Objective newLISP also enhances newLISP by providing convenient and safe ;; macros for deep reference access.

;;

With Objective newLISP it is possible to take full advantage of everything ;; object-oriented programming has to offer.

;;

Conventions

;; There are very few conventions in ObjNL, but there are some: ;; ;;

Requirements

;; newLISP 10.1.9 or higher is strongly recommended, but any version after 10.1 should work. ;;

Version history

;; 1.0 • initial release ;; @syntax ObjNL ;;

'ObjNL' is the root class for Objective newLISP. All other classes ultimately ;; inherit from it. It defines several instance and class variables:

;; (set 'ObjNL:@super nil ; ObjNL has no super class 'ObjNL:@self ObjNL ; similar to ObjC, can be instance or class 'ObjNL:@self-sym 'ObjNL ; symbol referencing name of this context 'ObjNL:@class ObjNL ; always refers to class, never instance 'ObjNL:@interfaces (list ObjNL) ; ObjNL implements ObjNL 'ObjNL:@rc 1 ; the object's retain (or 'reference') count ) (context ObjNL) ;; @syntax (ObjNL:ObjNL) ;;

The constructor is the default function. It is called by 'instantiate'.

;;

The default implementation simply returns 'true'.

(define (ObjNL:ObjNL) true) ;; @syntax (ObjNL:dealloc) ;;

Called by 'deallocate' to give the object an opportunity to release resources and objects.

(define (ObjNL:dealloc)) ;; @syntax (ObjNL:equals ) ;;

Provides a method for classes to define what it means for objects to be equal.

;;

The default implementation returns 'true' if two objects are the same instance.

(define (ObjNL:equals obj) (= obj @self)) (context MAIN) ; it's possible to even implement reference counting :-p ;; @syntax (new-class [ []]) ;; @param The name of the class ;; @param The superclass, accessible through '@super' ;; @param Any contexts to "mixin", accessible through '@interfaces' ;; @return The context of the new class created. (define (new-class sym-class (super ObjNL) (interfaces '()) , class) (set 'class (new super sym-class) 'class:@super super 'class:@class class 'class:@self class 'class:@self-sym sym-class ) ; NOTE: newLISP Bug? Why does pushing to the back result in odd behavior? ; (push class class:@interfaces -1) (push class class:@interfaces) (dolist (iface interfaces) (setf iface (eval iface)) (new iface sym-class) (push iface class:@interfaces) ) class ) ;; @syntax (instantiate [ ...]) ;;

Returns a new instance of by calling its ;; constructor and passing in any arguments. If the constructor ;; returns nil then the instance is deallocated and nil is returned.

;;

The returned object must be deallocated using the 'deallocate' ;; function.

(define (instantiate class) (letn ( obj-sym (sym (string class "#" (inc class:@instance-counter))) obj (new class obj-sym) ) ; set these prior to calling the constructor (set 'obj:@self obj 'obj:@self-sym obj-sym) (if (apply obj $args) obj (begin (deallocate obj) nil) ) ) ) ;; @syntax (add-interface ) ;;

Uses the function 'new' to add to the object and ;; adds the interface to s '@interfaces'.

(define (add-interface iface obj) (new iface obj:@self-sym) (push iface obj:@interfaces) ) ;; @syntax (deallocate ) ;;

Calls the objects 'dealloc' method and then 'delete''s the object.

;;

NOTE: On versions of newLISP prior to 10.1.9 this is a fairly slow ;; operation, make sure to use at least version 10.1.9 with Objective newLISP.

(define (deallocate obj) (obj:dealloc) (let (obj-sym obj:@self-sym) (delete obj-sym nil) ; delete the context (delete obj-sym nil) ; delete the symbol in MAIN ) ) ;; @syntax (implements? ) ;; @return true or nil as to whether this implements . (define (implements? iface obj) (not (nil? (find iface obj:@interfaces))) ) ;; @syntax (retain ) ;;

Increment's 's retain count and returns the object.

(define (retain obj) (inc obj:@rc) obj ) ;; @syntax (release ) ;;

Decrement's 's retain count. Deallocates the object if the retain count hits zero.

(define (release obj) (when (zero? (dec obj:@rc)) (deallocate obj) ) ) ;; @syntax (autorelease ) ;;

Adds to the current 'MAIN:@autorelease' pool and returns the object.

(define (autorelease obj) (push obj (first @autorelease)) obj ) ;; @syntax (push-autorelease-pool) ;;

Pushes a new autorelease pool onto the 'MAIN:@autorelease' stack.

(define (push-autorelease-pool) (push '() @autorelease) ) ;; @syntax (pop-autorelease-pool) ;;

Pops the current 'MAIN:@autorelease' pool and releases the objects in it.

(define (pop-autorelease-pool , obj) (dolist (obj (pop @autorelease)) (release obj) ) ) (global 'new-class 'instantiate 'deallocate 'implements? 'retain 'release 'autorelease 'push-autorelease-pool 'pop-autorelease-pool '@autorelease) ;; @syntax (. [ []]) ;;

The dot macro is used for "deep value access":

;; example: ;;
;; (new-class 'Foo)
;; (new-class 'Bar)
;; (context Bar)
;; (define (Bar:Bar f)
;; 	(setf foo f)
;; 	true ; -> do not deallocate us if 'f' is nil
;; )
;; (context Foo)
;; (define (Foo:Foo b)
;; 	(setf bar b)
;; 	true ; -> do not deallocate us if 'b' is nil
;; )
;; (context MAIN)
;; (setf f (instantiate Foo (instantiate Bar)))
;; (set (.& f bar foo) f) ; => Foo#1
;; (. f bar foo bar)      ; => Bar#1
(context '.) (define-macro (.:. obj) (doargs (field) (setf obj (eval (sym field (eval obj) nil))) ) ) ;; @syntax (.& [ []]) ;;

The dot-reference macro is similar to the dot macro, except it returns the ;; context-qualified symbol for the final field instead of its value ("deep symbol access"). ;; This allows you to combine it with 'set'.

;; @see '.' macro for example usage. (context '.&) (define-macro (.&:.& obj) (doargs (field) (setf obj (sym field (eval obj))) ) ) (context MAIN)