#|=head1 Inside GOLD (versions 1 and 2) These pages describe all the details behing Graham's GOLD system. Those pages (chp 17 of Graham) should be read first. =head2 Conventions A function named C or C is a preliminary version of a function that will be refined, later. Anything starting with C<$> is a UNIX shell command; e.g. $ ls abcd.lisp counts.lisp dist.lisp macros.lisp nb.lisp bounded.lisp data.lisp gold.lisp make.lisp random.lisp class.lisp deftest.lisp knn.lisp misc.lisp rbst.lisp Anything starting with C> is a LISP REPL command; e.g. > (+ 1 1) 2 =head1 Load Code |# (defun make () (handler-bind ((style-warning #'muffle-warning)) (load "gold.lisp"))) #| =head1 Gold (v1) =head2 Accessing a raw value |# (defun rget% (prop obj) (multiple-value-bind (val in) (gethash prop obj) (if in (values val in) (let ((par (gethash :parent obj))) (and par ; if you have a parent (rget prop par)))))) ; go fetch! #| To understand the above, you need to know that C returns not one, but two, values and the second of these values is a flag that is non-nil if the key was not in the hash table (so now we can distinguish between a missing slot and a slot that is present, but contains nil). The form C<(multiple-value-bind values call progn)> catches the output C from C and makes the available for the remaining code in C. The form C<(values val in)> is the way of defining multiple returning values. Finally, the form C<:parent> is a C. Keywords evaluate to themselves and are global to the entire LISP program (note quite- they are global to the I inside of which they are defined). =head2 Running a Method |# (defun tell% (obj message &rest args) (apply (rget% message obj) obj args)) #| =head2 Demo Code |# (defun !oo0 () "expect radius = 2 area = 12.566370614359172d0" (with-output-to-string (str) (!oo0-prim str))) #| Aside: I. When writing tests, it is often useful to catch the output from print statements. The above form I defined a stream we can pass to sub-routines. Those sub-routines can, optionally, use that stream to write their output (see example below- note that the next function can still run properly, even if it is not within an C form. |# (defun !oo0-prim (&optional (str t)) (let (circle-class our-circle) (setf circle-class (make-hash-table) our-circle (make-hash-table) (gethash :parent our-circle) circle-class (gethash 'radius our-circle) 2 (gethash 'area circle-class) #'(lambda (x) (* pi (expt (rget 'radius x) 2 )))) (format str "~&radius = ~a area = ~a~%" (rget% 'radius our-circle) (tell% our-circle 'area)))) #| Note how, in the above, we set the parent of an object with C<:parent>. Output: "radius = 2 area = 12.566370614359172d0 " =head2 Analysis As Graham says, before going on to improve this program, it's worth pausing to consider what we have done. With eight lines of code we have made plain old pre-CLOS Lisp into an object-oriented language. How did we manage to achieve such a feat? There must be some sort of trick involved, to implement object-oriented programming in eight lines of code. There is a trick, but it is not a programming trick. The trick is, Lisp already was an object-oriented language, or rather, something more general. All we had to do was put a new facade on the abstractions that were already there. Having said that, there are some issues with the above. =over 8 =item * Everything hashed. not the fastest option =item * Ugly syntax. error prone. ideally, want someway to batch common operations =back Has some OO features: association, polymorphishm, inheritance Lacks others: =over 8 =item * No concept of "class" seperaete to "instance" (anything can be something anything else can inherit from =item * No encapulation : variables referenced different to methods =back =head2 Fixes (Which are wrong) Adding encapsulation: (defun tell%% (obj message &rest args) (let ((what (rget message obj))) (if (functionp what) (apply (rget message obj) obj args) what))) Advantage: now we can get everything via C. Disadvantage: Setf is a problem. E.g. can't > (setf (tell *timm* 'age) 22) =head1 Digressions Support notes on upcoming code. =head2 Customizing Setf (Graham, p100) By making the first argument to defun of function C a list of the form C<(setf x )>, you define what happens when the first argument to setf is a call to C. The following pair of functions defines primo as a synonym for car: (defun primo (1st) (car 1st)) (defun (setf primo) (val 1st) (setf (car 1st) val)) In the definition of a function whose name is of the form C<(setf x)>, the first parameter represents the new value, and the remaining parameters represent arguments to C. Now any setf of primo will be a call to the latter function above: > (let ((x (list 'a 5b >c))) (setf (primo x) 480) x) (480 B C) =head1 Gold (v2) Mulitple inheritance means that if code exists in more than one parent, we must define a search order. When we want some code, we search in that order and return the I thing we find, in that order. In the following example, we have a list of parents (initally C and then C). |# (defun rget%%% (prop obj) (dolist (c (precedence% obj)) (multiple-value-bind (val in) (gethash prop c) (if in (return (values val in)))))) #| This is called I. It begins by calling C to build a list representing the objects encountered in a depth-first traversal: =over 8 =item * If any of the objects share parents, there will be duplicates in this list. =item * If we preserve only the last of each set of duplicates, we will get a precedence list in the specialized oder. =item * The Common Lisp function delete-duplicates is defined to behave this way, so if we just call it on the result of the depth-first traversal, we will get the correct precedence list. =back |# (defun precedence% (obj) (labels ((traverse (x) (cons x (mapcan #'traverse (gethash :parents x))))) (delete-duplicates (traverse obj)))) (defun !oo1 () (let (scoundrel patriot patriotic-scoundrel) (setf scoundrel (make-hash-table) patriot (make-hash-table) patriotic-scoundrel (make-hash-table) (gethash 'serves scoundrel) 'self (gethash 'serves patriot) 'country (gethash :parents patriotic-scoundrel) (list scoundrel patriot)) (rget%%% 'serves patriotic-scoundrel))) #| =head1 Gold (v3) For the next version of Gold, see Gold-v3. =head1 Installation =head2 Download Source code: $ wget http://goo.gl/QuBNH This doco: $ wget http://goo.gl/OSB94 =head2 Execute First load into LISP: > (load "gold.lisp") ; ignore warning messages Reloads: > (make) |#