Monthly Archives: October 2013

Exception handling in Lisp, as seen from C++, Part 2

We’ve talked a bit about conditions in Lisp, and how they differ from those in C++.  It’s helpful, though, to provide an example or two.

So, to demonstrate conditions, we’ll start with a simple program.  It opens a file on disc and reads pairs of numbers from it.  For each pair it displays the ratio of the first number to the second number.  Let’s call this the “My First Lisp Homework Problem” version of the code, because, while it does kind of work, it needs some improvements for real applications.

 
;; Some examples of using conditions in Lisp code.
;;
(declaim (optimize (debug 3) (safety 3)))

(defun condition-demo (pathname)
  (format t "Starting to analyse file ~A~%" pathname)
  (with-open-file (s pathname :direction :input)
    (analyse-stream s)))

;; Goes through the stream, dividing entries one by the next
(defun analyse-stream (stream)
  (do ()
      (nil)
    (let ((num1 (get-next-number stream))
          (num2 (get-next-number stream)))
      (format t "The ratio of ~A to ~A is ~A~%" num1 num2 (/ num1 num2)))))

;; Returns the next object in the stream
(defun get-next-number (stream)
  (read stream))

If you run condition-demo on a disc file that contains numbers, it will produce ratios and display them on the screen.  However, this code can’t handle anything unexpected.  If the pathname doesn’t exist, or if it doesn’t contain only numbers, or if the file isn’t infinitely long, it will, sooner or later, generate an unhandled error.

So, let’s start with the most obvious case.  The pathname is unreadable.  Either it doesn’t exist, or the process doesn’t have permission to open the file for reading.  In that case, a condition of the type ‘file-error is signaled.  Condition objects typically have a printable form, so we can print them in a format statement to help the user understand the problem.

 
;; Some examples of using conditions in Lisp code.
;;
(declaim (optimize (debug 3) (safety 3)))

(defun condition-demo (pathname)
  (format t "Starting to analyse file ~A~%" pathname)
  (handler-case
      (with-open-file (s pathname :direction :input)
        (analyse-stream s))
    (file-error (c)
        (format t "A file error was encountered while trying to analyse ~A.~%The error returned was:~%~A~%" pathname c))))

;; Goes through the stream, dividing entries one by the next
(defun analyse-stream (stream)
  (do ()
      (nil)
    (let ((num1 (get-next-number stream))
          (num2 (get-next-number stream)))
      (format t "The ratio of ~A to ~A is ~A~%" num1 num2 (/ num1 num2)))))

;; Returns the next object in the stream
(defun get-next-number (stream)
  (read stream))

Now, if the file is not present on disc, or is not readable, an error message is delivered to the screen, and the function exits cleanly.  That, though, is only the first of the things that can go wrong, and shows only the basic throw/catch style handling of conditions similar to those familiar to C++ programmers.  In a later post, we’ll discuss another way conditions can be handled.

Exception handling in Lisp, as seen from C++, Part 1

In earlier series of articles, I’ve been talking about Lisp language features as they might appear to a C++ programmer.  I went over the power of Lisp macros, and talked about object-oriented programming in Lisp.

Now, we move on to exception handling, referred to in Lisp as conditions.  In the simplest case, conditions look like C++ exceptions.  The programmer wraps a piece of code in a “handler-case” block, the equivalent of try{} in C++.  Somewhere down the call stack, a function throws the exception, and the stack unwinds, looking for an exception handler at each level.  When it finds one, the unwinding stops, the handler is executed, and code continues after the handler (unless the handler itself throws another exception and the stack unwinding proceeds anew).  That’s what the simple case looks like in Lisp, but that’s not the true story, it’s just a one common programming model for conditions.

So, what really happens?  Well, conditions can be addressed by handlers or by restarts.  When a condition is signaled, the environment in which the code is executing is examined.  If the closest handler is a restart, the appropriate function is invoked.  Crucially, Lisp does not unwind the stack in this instance.  The restart runs as if called as a function from the point where the error occured (but with the restart handler unbound, so it won’t be called recursively if the restart signals the exception again).  The restart can, in fact, fix the error and allow flow to continue after the signal (the equivalent of the C++ throw).

With this available, the programmer can, at one point in the code, decide what to do about conditions signaled within the context.  (S)he can decide to operate on the error and then allow the code to continue from the point where the error was signaled, or cause the code to unwind the stack to a handler, or do nothing, and let the signals be controlled by the enclosing context.  While the code that signals the error generally has to be written with the possibility of restarts in mind, it does provide an interesting way to handle exceptional cases.

I will be providing examples in subsequent posts.

Formatting code for the blog

In case people were wondering how the code I’ve been posting has been formatted, here’s the procedure.

This blog is running on a WordPress installation.  I’ve installed the “Raw HTML” plugin.  This allows me to insert HTML directly into postings by surrounding them with “[raw]” and “[/raw]” in the text input mode.

I program in emacs, so I just needed an emacs package to convert the buffer to HTML.  I started out trying to use the built-in htmlfontify-buffer function, but it produced HTML with CSS elements that affected the rest of the page.  So, after looking around a bit, I settled on htmlize, which is available at this location.

So, load the code to be rendered, load the htmlize library, and run htmlize-buffer.  This creates a new buffer holding the HTML version of the displayed text, ready to be copied into the blogging software.

Object-oriented Lisp programming, as seen from C++. Part 3

Before I leave the topic of objects in Lisp vs. C++, I’ll bring up one more remarkable feature of Lisp.  This probably belongs to the category of things the C++ programmer never even imagined was a possibility.

It is possible, in a running Lisp instance, to redefine a class.  That is, the programmer can decide that a class was incorrectly implemented, and that it has to be redefined.  In this event, the programmer can supply code that lazily converts objects from the old class definition to the new one, without exiting the running instance.  Rather than describing the mechanics of this, I refer the interested reader to this page from the Common Lisp HyperSpec.

For a long-running, stateful C++ program, redefining classes would require saving state to disc, shutting down the binary, then starting the new binary and having it reload its state into the new classes.  It’s fairly remarkable that Lisp allows the programmer to define the conversion in the running core, so that it can simply lazily convert objects as they are encountered, allowing the program to continue running without interruption.

Object-oriented Lisp programming, as seen from C++. Part 2

So far, we’ve looked at simple generic functions, and how they compare to C++ methods.  There are, however, some generic functions that add interesting and useful features not obviously available in C++.  These include the :before, :after, and :around generic functions.

Looking at the code we supplied in the first part of this series, we see some duplicated code.  We create a new class for managed employees, and rebuild the hierarchy with those.  Then, we decide that as we further specialize the classes, the printing of them just adds further fields.  The new code now looks like this:

 
;; Polymorphism example

(defpackage :POLYMORPHISM
  (:use :COMMON-LISP)
)

(in-package :POLYMORPHISM)

(declaim (optimize (debug 3) (safety 3)))
; (declaim (optimize (debug 0) (safety 0) (speed 3)))


(defclass employee ()
  ((name                :accessor get-name
                        :initarg :name)
   (id                  :accessor get-id
                        :initarg :id)))

(defclass supervised-employee (employee)
  ((supervisor          :accessor get-supervisor
                        :initform nil
                        :initarg :supervisor)))

(defclass manager (supervised-employee)
  ((underlings          :accessor get-underlings
                        :initform nil
                        :initarg :underlings)))

(defclass underling (supervised-employee)
  ())

(defgeneric myprint (stream object)
  (:documentation "Prints to 'stream' some information about 'object'."))

(defparameter *printing-border* nil)

(defmethod myprint :around (stream (object employee))
  (when *printing-border*
    (format stream "~A~%" *printing-border*))
  (prog1
      (call-next-method)
    (when *printing-border*
      (format stream "~A~%" *printing-border*))))

(defmethod myprint (stream (object employee))
  (format stream "Employee name: ~A~%" (get-name object))
  (format stream "Employee ID: ~D~%" (get-id object)))

(defmethod myprint (stream (object supervised-employee))
  (call-next-method)
  (let ((supervisor (get-supervisor object)))
    (cond 
      (supervisor
       (format stream "Supervisor ID: ~D~%" (get-id supervisor)))
      (t
       (format stream "No supervisor~%")))))

(defmethod myprint (stream (object manager))
  (call-next-method)
  (format stream 
          "Supervising IDs:~{ ~D~}.~%" 
          (mapcar 'get-id (get-underlings object))))

(defmethod myprint (stream (object underling))
  (call-next-method))

(defun test-system ()
  (let* ((orc-1 (make-instance 'underling :name "Orc-1" :id 1))
         (orc-2 (make-instance 'underling :name "Orc-2" :id 2))
         (orc-3 (make-instance 'underling :name "Orc-3" :id 3))
         (orc-4 (make-instance 'underling :name "Orc-4" :id 4))
         (uruk-1 (make-instance 'manager :name "Uruk-1" :id 5 
                                :underlings (list orc-1 orc-2)))
         (uruk-2 (make-instance 'manager :name "Uruk-2" :id 6 
                                :underlings (list orc-3 orc-4)))
         (saruman (make-instance 'manager :name "Saruman" :id 7
                                 :underlings (list uruk-1 uruk-2))))
    (setf (get-supervisor orc-1) uruk-1
          (get-supervisor orc-2) uruk-1)
    (setf (get-supervisor orc-3) uruk-2
          (get-supervisor orc-4) uruk-2)
    (setf (get-supervisor uruk-1) saruman
          (get-supervisor uruk-2) saruman)

    (myprint t orc-1)
    (format t "~%~%")
    (myprint t orc-2)
    (format t "~%~%")
    (myprint t orc-3)
    (format t "~%~%")
    (myprint t orc-4)
    (format t "~%~%")
    (myprint t uruk-1)
    (format t "~%~%")
    (myprint t uruk-2)
    (format t "~%~%")
    (myprint t saruman)
    (format t "~%~%")))

Now, the myprint method for supervised-employee objects calls a function call-next-method.  This causes it to invoke, with the same arguments, the next defined method from a parent class, if any.  So, when the myprint method for manager objects is called, it first calls the myprint method for supervised-employee objects, which in turn calls the myprint method for employee objects.  In C++, one would do this with an explicit class name in the invocation.  There’s another defined method here, though, the :around method for employee objects.  This allows the programmer to insert an easy shim around the named method itself, where certain things could be done.  In this example, it optionally prints text above and below the record.  In other cases, it could be used to perform some sort of global sanity checking on passed arguments or returned values, or it could be used to insert a profiling or timing loop, without the need to scour through and edit the corresponding methods for all classes in the inheritance group.  This is a useful feature.  In fact, in some of my C++ code I’ve been known to write methods with an empty springboard function, purely so that I could add in general inspection code later in the style of an :around method.

It’s worth pointing out, too, that the myprint method for underling objects is entirely superfluous.  If that method were not defined, the invocation of myprint on it would look first for a supervised-employee method, and if there was none, would then look for an employee method.

The :before and :after methods are similarly useful, for taking action before or after a method is invoked.  A particularly useful one is to create an :after method for the initialize-instance method.  The initialize-instance method is the method that is invoked to construct objects.  It can fill in slots based on :initform or :initarg arguments, but doesn’t do more than that.  Think of it like the initialization list in a C++ constructor.  An :after method is like the body of the constructor.  It can take action based on the initialized slots.  This might include opening a file, building a data structure, or doing something more that, conceptually, you want as part of make-instance, but which is more complex than a simple assignment.