Method Versions — v0.1.2011.05.18 May 18th, 2011
Patrick Stein

Edit: After re-reading some of the ContextL papers, I believe that I am actually just going to use ContextL as it’s a much more flexible superset of this library. I will probably still keep this library published as an example of a non-trivial, but glarkable, method combination.

I am releasing a new library that allows one to dispatch generic methods based on the value of a global parameter.

There are situations where one might like to dispatch a method on some information other than the required parameters of the method. For many situations, it is sufficient to switch between those methods based on some external parameter. The method-versions library allows one to do just that.

Obtaining

Internationalization Example

In this example, we do a silly form of internationalization. To that end, we will use English as the default language and define some other languages.

 (method-versions:define-method-version latin)
 (method-versions:define-method-version pig-latin)
 (method-versions:define-method-version french latin)
 (method-versions:define-method-version spanish latin)

We will prepare a language parameter and a welcome method that is versioned on the language.

 (declaim (special *language*))
 (defparameter *language* nil)
 
 (defgeneric welcome ()
   (:method-combination method-versions:method-version-method-combination
                        *language*))

And, we define welcome methods for the various languages (accidentally forgetting spanish).

 (defmethod welcome () :welcome)
 (defmethod welcome :latin     () :velkominum)
 (defmethod welcome :pig-latin () :elcomeway)
 (defmethod welcome :french    () :bonjour)

Then, we will try each of the languages in turn.

 (mapcar #'(lambda (ll)
             (let ((*language* ll))
               (welcome)))
         '(nil :latin :pig-latin :french :spanish))
 => (:welcome :velkominum :elcomeway :bonjour :velkominum)

USerial — v0.6.2011.05.12 May 12th, 2011
Patrick Stein

The latest release of my USerial library provides a way to make a simple serialize/unserialize pair for a list where every item can be serialized using the same key.

(make-list-serializer :list-of-integers :uint32)

(with-buffer (make-buffer)
  (serialize :list-of-integers '(1 2 3 4 5 6 7))
  (buffer-rewind)
  (reduce #'+ (unserialize :list-of-integers))) => 28

Here is the latest:

Delayed Evaluation Across Packages April 24th, 2011
Patrick Stein

For my networking layer library, I wanted to provide ubiquitous logging. At the same time, I did not want to tie the application to my choice of logging library. I wanted the user to be able to pass me a function where I could give them a logging category and something to log.

(in-package :unet)

(defvar *logger-function* nil)

(defmacro log-it (category thing-to-log)
  `(when *logger-function*
     (funcall *logger-function* ,category ,thing-to-log)))

This seems simple enough, right? Now, throughout my code, I can do things like:

(log-it :incoming-packet packet)
(log-it :list-of-unacked-packets (get-list-of-unacked-packets))
(etc)

The application can register a *logger-function* something like this:

(defun app-log-network-msgs (category thing-to-log)
  (cl-log:log-message category thing-to-log))

Here’s the problem though: most (all?) logging libraries are good about not evaluating any arguments beyond the category unless something is actually listening for messages of that category. This makes it reasonable to do stuff like this and only take the speed hit when it’s actually important to do so:

(log-it :excruciating-detail
        (mapcar #'get-excrutiating-detail (append everyone everything)))

With my macro above, I have no way of knowing whether something is listening on a particular category or not. Further, most logging libraries don’t offer a way to query that sort of information.

What to do?

What I wanted to do was to pass a macro from the application package into my networking library instead of passing a function. I spent way too long trying to find a way to make this work (especially considering I ran into the same trouble in October, 2009 trying to use some combination of (macroexpand ...) and (eval ...) to let the caller decide which forms to execute).

All it took was posting to comp.lang.lisp to answer my own question: Closures. I changed my macro to create a closure:

(defmacro log-it (category thing-to-log)
  `(when *logger-function*
     (funcall *logger-function* ,category #'(lambda () ,thing-to-log))))

Now, the application’s logger function changes slightly and my forms are only evaluated when the logging library evaluates them:

(defun app-log-network-msgs (category thing-to-log-generator)
  (cl-log:log-message category (funcall thing-to-log-generator))

Hopefully, I will remember next time I run into this.

USerial — v0.5.2011.04.21 released April 21st, 2011
Patrick Stein

I am releasing a new version of my USerial library.

This release extends the (make-slot-serializer ...) and (make-accessor-serializer ...) so that you can pass an existing instance into the unserializers with the :object parameter rather than having the factory form allocate a new instance.

This release also provides ways to serialize and unserialize from slots and accessors in other code to allow even greater flexibility:

(serialize-accessors* (:string person-name
                       :uint8  person-age
                       :string person-hair-color)
     *person-instance* :buffer buffer)

(unserialize-slots* (:string name
                     :uint8  age
                     :string hair-color)
     *person-instance* :buffer buffer)

The serialize-slots* and unserialize-accessors* macros are also available.

Here is the latest:

Edit: Special thanks to Elliott Slaughter. The above improvements came out of recommendations from him.

USerial — v0.4.2011.04.11 April 12th, 2011
Patrick Stein

Made an addition to the USerial library to support logging of binary messages (so far only cl-log supported).

(serialize-log :log-category :uint32 uint-to-log :string "string to log" ...)

Here is the latest tarball: userial_0.4.2011.04.11.tar.gz and its signature: userial_0.4.2011.04.11.tar.gz.asc.

Updates In Email

Email:

l