Object Programming in Lisp Observation #1

January 5, 2009

When developing with the object framework, I end up excreting all the side-effect-free (SEF) code from the object. Remember the reason for an object is to encapsulate state.

(defun format-results (result-totals)
  (destructuring-bind (result-totals asserts passes errors elapses) result-totals
    (list
     (format nil "~A Total Test(s)" result-totals)
     (format nil "~A Assertions" asserts)
     (format nil "~A Passes" passes)
     (format nil "~A Failures" (- asserts passes))
     (format nil "~A Errors" errors)
     (format nil "~,3F Second(s)" elapses))))

(defun result-sums (result-totals)
  (loop for (nil assert pass error elapse) in result-totals
        summing assert into asserts
        summing pass into passes
        summing error into errors
        summing elapse into elapses
        finally
        (return
         (list (length result-totals) asserts passes errors elapses))))

(defobj results-model
  (:members ((database nil)))
  (:methods ((:get-result-totals ()
                  (format-results (result-sums (retrieve-totals database))))

             (:connect-db (db)
               (setf database db))

             (:insert-result-data (result-data)
               (add-result database result-data))))

This method really it makes it easier to test the SEF functions. When using the object’s methods for interacting with state during tdd, you have to maintain a reference to the object. By excreting the functionality out of the object, you no longer need the reference. You just test for functionality.

(test-fixture
 :format-results
 (:tests
   (should-return-format-results-empty
    (assert-equal '("0 Total Test(s)" "0 Assertions" "0 Passes" "0 Failures" "0 Errors"
           "0.000 Second(s)") (format-results (list 0 0 0 0 0))))))

(test-fixture
 :result-sums
 (:tests
   (should-return-no-sums
    (assert-equal '(1 0 0 0 0) (result-sums (list (list :no-name 0 0 0 0)))))

   (should-return-sums
    (assert-equal '(2 1 1 1 0.15100001) (result-sums (list (list :t1 0 0 1 0.001) (list :t2 1 1 0 0.15)))))))

(test-fixture
 :results-model
 (:setup ((db (make-database))
          (model (make-results-model)))
   (connect-db model db))
 (:tests
   (should-transmit-result-totals
    (let ((totals '("0 Total Test(s)" "0 Assertions" "0 Passes" "0 Failures" "0 Errors"
           "0.000 Second(s)")))
      (assert-equal totals (get-result-totals model))))

   (should-insert-result-data
    (let ((totals '("1 Total Test(s)" "1 Assertions" "1 Passes" "0 Failures" "0 Errors"
           "0.000 Second(s)")))
      (insert-result-data model (list :no-name 1 1 0 0.0))
      (assert-equal totals (get-result-totals model))))))

This is very conducive to programming incrementally.

The function we want to look at is get-result-totals. This function retrieve the totals from the database. We pass this then to result-sums. It’s results are then passed to format-results.

  1. Retrieve results from database.
  2. Sum results.
  3. Format results.

Happy January!


An Object in Lisp. Part 6

January 2, 2009

What a way to start a little update on the the object framework that I have been pushing on.

The last update talked about ‘destructuring-bind‘. I had an epipheny the other day when I was trying to tdd a way to create generic functions for the framework. That led me to throwing away the destructuring-bind.

In order to explain my reasoning we need to go back to the representation of an object in the framework. The representation is a list of closures referenced by properties.

(defun hello (obj &rest args)
  (apply (getf obj :hello) args)

(defvar hold1 (list :hello (lambda () (print 'hello))))
(defvar hold2 (list :hello (lambda (x) (print x))))

The above lists can be accessed with a getf that retrieves the lambda from the list. To execute the lambda just apply. The hello defun above will handle either list as obj.

Something that I now know,  is that the lambda does its own destructuring-bind on its parameter list.  So here is how I create the accessor function for all objects  (generic and multiple signatures):

(defmacro make-property (name)
  (let ((g (gensym)))
    (multiple-value-bind (sym-name key-name) (symbol-and-keyword name)
      `(progn
         (if (not (fboundp ',sym-name))
             (defun ,sym-name (obj &rest ,g)
               (block ,sym-name
                 (apply (getf obj ,key-name) ,g)))))))

Happy New Year!

gutzofter


Lisp Programming Trick #2

December 22, 2008

Recursive Condition

This little trick hit me when I was perusing PCL. Specifically, the when selector.

(defun rec-cond (key)
  (cond
    ((eq key :before) (format nil "executing before" ))
    ((eq key :during) (format nil "executing during" ))
    ((eq key :after) (format nil "executing after" ))

    ((eq key :set-up) (rec-cond :before))
    ((eq key :init) (rec-cond :before))

    ((eq key :teardown) (rec-cond :after))

    (t (error (format nil "unknown command: ~A" key))))

This overloads keyword symbols so that you more than one keyword can execute the same code. Read the rest of this entry »


An Object in Lisp. Part 5

December 18, 2008

Destructuring Bind

… binds the variables specified in lambda-list to the corresponding values in the tree structure resulting from the evaluation of expression; then destructuring-bind evaluates forms.

‘destructuring-bind‘ is a wonderful macro. The first thing you do is hand it a lambda list and a list of values. It acts like a pattern matcher. If the list of values does not match up to the lambda list it will throw an error.

Here is what I’m talking about:

(defun test-d-bind (&rest args)
  (destructuring-bind (a &optional (b 3) &rest x &key c (d a)) args
    (list a b c d x))
(test-d-bind nil)
;>>(NIL 3 NIL NIL NIL)
(test-d-bind 1 6 :d 8 :c 9 :d 10)
;>>(1 6 9 8 (:D 8 :C 9 :D 10)

Read the rest of this entry »


And Behind Door Number 2 Is . . . A Leaky Abstraction

December 11, 2008

Liskov Substitution  Principle (LSP):

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

My Bogus was an attempt to abstract a method by which I could get access to a variable using a syntatic-sugar idiom. The abstraction leaks. Here is how it leaks:

(should-maintain-scope-of-inner-property
       (with-property (value 24)
         (value :is (incf (value))))
       (assert-equal 25 (value)))

‘incf’ is a destructive operation.

Read the rest of this entry »


Abstraction of an encapsulted property in Lisp

December 9, 2008

Abstraction (computer science): is a mechanism and practice to reduce and factor out details so that one can focus on a few concepts at a time.

If you read Lisp Trick #1, hopefully, you saw a pretty good abstraction. What it was doing was abstracting out the setting and getting a value from a variable. As a bonus, because of the way &key parameter keyword can be used, we were able to differentiate whether you were getting the value or setting the variable. Another bonus is that the variable we are getting and setting is encapsulated.

Usually an abstraction is created with the use of macros. Now I’ve made an even larger abstraction of the setter/getter abstraction.

Read the rest of this entry »


Recursive Tree Rabbit Holes

December 8, 2008
  1. WHY? When using my own home grown object system, I found out that it could not handle keyword parameters.
  2. Problem. Take a parameter list and remove keyword parameters.
  3. Solution. Parse the argument list to the functions, remove and if needed replace keyword.
  4. Example. Need to remove &key and replace with keyword and variable.
    • (a b) => (a b)
    • (a (b)) => (a (b))
    • (&key a) => (:a a)
    • (&key a b) => (:a a :b b)
    • (a &key b) => (a :b b)
    • (a (&key b)) => (a (:b b))

Read the rest of this entry »