Prog. Languages

Notes for WVU cs310, Programming lanuages, Spring 2011.

Gold1 and 2:
Basic OO.

Gold3:
Intermediary OO.

Gold4:
This time, we mean it.

Inside GOLD (version 1 and 2)


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.

Conventions

A function named x% or x%% is a preliminary version of a function that will be refined, later.

Anything starting with $ 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 > is a LISP REPL command; e.g.

 > (+ 1 1)
 2


Load Code

  (defun make ()
    (handler-bind ((style-warning #'muffle-warning))
      (load "gold.lisp")))


Gold (v1)

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 gethash 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 (multiple-value-bind values call progn) catches the output values from call and makes the available for the remaining code in progn.

The form (values val in) is the way of defining multiple returning values.

Finally, the form :parent is a keyword. Keywords evaluate to themselves and are global to the entire LISP program (note quite- they are global to the package inside of which they are defined).

Running a Method

  (defun tell% (obj message &rest args)
   (apply (rget% message obj) obj args))

Demo Code

  (defun !oo0 ()
    "expect radius = 2 area = 12.566370614359172d0"
    (with-output-to-string (str)
      (!oo0-prim str)))

Aside: The String Trick. When writing tests, it is often useful to catch the output from print statements. The above form with-output-to-string 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 with-output-to-string 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 :parent.

Output:

 "radius = 2 area = 12.566370614359172d0
 "

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.

Has some OO features: association, polymorphishm, inheritance

Lacks others:

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 tell.

Disadvantage: Setf is a problem. E.g. can't

 > (setf (tell *timm* 'age) 22)


Digressions

Support notes on upcoming code.

Customizing Setf

(Graham, p100)

By making the first argument to defun of function f a list of the form (setf x ), you define what happens when the first argument to setf is a call to f.

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 (setf x), the first parameter represents the new value, and the remaining parameters represent arguments to f.

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)


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 first thing we find, in that order.

In the following example, we have a list of parents (initally first and then country).

  (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 precedence ordering.

It begins by calling traverse to build a list representing the objects encountered in a depth-first traversal:

  (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)))


Gold (v3)

For the next version of Gold, see Gold-v3.


Installation

Download

Source code:

 $ wget http://goo.gl/QuBNH

This doco:

 $ wget http://goo.gl/OSB94

Execute

First load into LISP:

 > (load "gold.lisp") ; ignore warning messages

Reloads:

 > (make)


Author

Tim Menzies (tim@menzies.us)


Copyright

Share and enjoy.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.