August 18, 2013

The Human Consequences of Dynamic Typing

I agree with Jay McCarthy that static typing is anti-human. I think I can give a slightly more elaborate example of a program that every static type checker must reject, but that is nevertheless correct. Everything below is valid Common Lisp:

(defclass person ()
  ((name :initarg :name
         :accessor person-name)))
(defmethod display ((p person))
  (format t "Person: Name ~S, address ~S."
            (person-name p)
            (person-address p)))

A static type checker must reject this program because there is no address field defined in the class person that person-address presumably refers to. However, below is a run of the program in a Lisp listener that runs to a correct completion without fatal (!) errors:

CL-USER 1 > (defvar *p* (make-instance 'person :name "Pascal"))
*P*
CL-USER 2 > (display *p*)
Error: Undefined function PERSON-ADDRESS called with arguments
(#<PERSON 4020059AB3>).
  1 (continue) Try invoking PERSON-ADDRESS again.
  2 Return some values from the call to PERSON-ADDRESS.
  3 Try invoking something other than PERSON-ADDRESS with the
    same arguments.
  4 Set the symbol-function of PERSON-ADDRESS to another
    function.
  5 (abort) Return to level 0.
  6 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for
other options.
CL-USER 3 : 1 > (defclass person ()
                  ((name    :initarg :name
                            :accessor person-name)
                   (address :initarg :address
                            :accessor person-address)))
#<STANDARD-CLASS PERSON 4130371F6B>
CL-USER 4 : 1 > :c 1
Error: The slot ADDRESS is unbound in the object
#<PERSON 41303DD973> (an instance of class
#<STANDARD-CLASS PERSON 4130371F6B>).
  1 (continue) Try reading slot ADDRESS again.
  2 Specify a value to use this time for slot ADDRESS.
  3 Specify a value to set slot ADDRESS to.
  4 (abort) Return to level 0.
  5 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for
other options.
CL-USER 5 : 1 > :c 3
Enter a form to be evaluated: "Belgium"
Person: Name "Pascal", address "Belgium".
NIL
CL-USER 6 > (display *p*)
Person: Name "Pascal", address "Belgium".
NIL

The "trick" is that the Lisp listener allows for interactive modification of a program while it is running. No static type checker can anticipate what modifications will be performed at runtime.

This is not a toy feature of Common Lisp, but something that many Lisp developers rely on. For example, my own ContextL library crucially relies on the very class redefinition feature I demonstrate above. Joe Marshall provides another account how such features can solve real-world problems.