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


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 »


Event Driven Architecture Programming in Lisp Part 1

December 3, 2008
  • What is Event Driven Architecture Programming?
    • It is the ability to inform interested objects (consumer/subscriber/sink) of changes of state from other objects (producer/subscriber/source) with events.
  • Why use this architecture?
    • There are two reason to use this architecture. One is to loosely couple the interacting objects. The second is that it is best-suited for use in an asynchronous context.
  • What are the components required?
    • A table that stores event-handlers
    • An interface to register a trigger of events.
    • An interface to register notification of an event trigger.
    • An interface to trigger events.
  • How to represent events?
    • Closures

Read the rest of this entry »


No we are not alone

December 3, 2008

Other lispers are also thinking and writing about objects and there implementations.

Jonathon Hawkes’s Building a true object-oriented system for Common Lisp is very interesting in the use of a-lists to store symbols for accessing his closures.


An Object in Lisp. Part 4

December 3, 2008
  • What does it mean to be an object?

  • A [computer] language mechanism for binding data with methods that operate on that data. Object (Computer Science).

    • Binding is the creation of a simple reference to something that is larger and more complicated and used frequently. Binding (Computer Science).

    • Data refers to a collection of facts usually collected as the result of […] processes within a computer system […]. Data.

    • […] a method usually consists of a sequence of statements to perform an action, a set of input parameters to customize those actions, and possibly an output value (called return value) of some kind. Methods (Computer Science).

So the following code by the above definitions must be an object.

(defun make-counter-obj ()
  (let ((counter 0))
    (list :increment-counter
          (lambda ()
            (incf counter))
          :get-counter
          (lambda ()
            counter))))

(defun increment-counter (obj)
  (funcall (getf obj :increment-counter)))

(defun get-counter (obj)
  (funcall (getf obj :get-counter))
  • ‘make-counter-obj’ creates the binding.
  • The ‘counter variable in ‘make-counter-obj’ is the storage for our data.
  • The two functions/methods ‘increment-counter’ and ‘get-counter’ are definitely methods of some kind, that allow us to operate on the ‘counter data.

Throughout this entire series it seemed to be inevitable that p-lists were to be used not only the actual storage mechanisms for the methods that act on the data, but also in the macros, p-lists are a major data structure.

Right now, I’m pretty comfortable with the implementation of this type of object. But already I can see future implementations. There is definitely another abstraction that can be pulled out of this. The ‘get-object-id’ function is a place to start. All object instances, that use my objects language, use this function. It could be considered part of a parent property.

Right now I want to start integrating Event Driven Architecture to further my cargo-cultism of Lisp.

Have a Happy Holiday,

Joe G


An Object in Lisp. Part 3

November 30, 2008

Adding constructors to object

The object needs to be able to implement default constructors. This required making changes to ‘defobj’ and ‘make-make-property’.

Specifically:

(defobj init
  (:init (d1 d2))
  (:methods
    ((:get-data () (values d1 d2))))
(defmacro defobj (name &rest class-data)
  (let ((members (when-select :members class-data))
        (methods (when-select :methods class-data))
        (constructors (when-select :init class-data)))
    `(progn
       (make-make-property ,name ,constructors ,members ,methods)
       (make-properties ,methods)))
(defmacro make-make-property (obj-name constructors members methods)
  (let ((property-list (loop for property in methods
                             append
                             (let ((name (first property))
                                   (args (second property))
                                   (commands (cddr property)))
                               `(,name (lambda ,args ,@commands))))))
    `(defun ,(make-name obj-name) (&rest args)
       (destructuring-bind ,constructors args
         (let ,members
           (list ,@property-list)))))

Using ‘defobj’ we extract out the code for the constructor. A change was made to ‘make-make-property by adding another parameter, ‘constructors. We now make a change to our code-generation to include args. then we use a destructuring-bind to bind the ‘constructors to ‘args. This will then wrap members and the list into a let. That is it.

Universal Identification

Because of later uses of objects, I need to have a way to create a specific identifier for each instance of a specific object. This isn’t a very good implementation of a universal identification generator. If anybody has a much better implementation, let me know. The implementation of a UID requires three new functions:

(defun unique-string-name (name-string)
  (format nil "~(~{~2,'0X~}~)"
          (coerce (sb-md5:md5sum-string
                   (sb-ext:native-namestring name-string))
                  'list)))

(defun uid (random-fn name-string)
  (unique-string-name
   (format nil "~a~a" name-string (funcall random-fn 1000000000))))

(defun object-id (obj)
  (funcall (getf obj :object-id)))

‘unique-string-name’ is a function taken from sb-cover. It takes a string and generates a funky string.

‘uid’ actually generates the uid. I pass in ‘random-fn, because I wanted to unit test this functions. The function will create a string from ‘name-string (object’s name) and randomly generate a number up to 1,000,000,000.

‘object-id’ is implemented as a function outside of the ‘defobj’ macro. All ‘defobj’ generated objects will implement an object-id in it’s list. This is done in ‘make-make-property’:

(defmacro make-make-property (obj-name constructors members methods)
  (let ((property-list (loop for property in methods
                             append
                             (let ((name (first property))
                                   (args (second property))
                                   (expressions (cddr property)))
                               `(,name (lambda ,args ,@expressions))))))
    `(defun ,(make-name obj-name) (&rest args)
       (destructuring-bind ,constructors args
         (let ((object-id (uid #'random ,(symbol-name obj-name))))
           (let ,members
             (list ,@property-list ,@(list :object-id `(lambda () object-id))))))))

All that was done was we close over the previous let with a new let that contains a variable object-id. The next part is when we create the list the contains the closures, we add the object-id closure.