Shadowing a CL function definition

Shadowing functions is useful for example when testing.
Suppose we want to build a tiny test suite around the following function:

 
(defparameter *appointment* ...)
 
(defun overdue-p ()
  (>= (get-universal-time) *appointment*))

Testing OVERDUE-P obviously requires us to test two branches: one where the appointment is overdue and one where it is not.

Let’s say that you can’t change *APPOINTMENT* in your testing context for whatever reasons (the reason here being that this example is ultra-contrived for simplicity).
Assuming a fixed value for *APPOINTMENT* we need to change the return value of CL:GET-UNIVERSAL-TIME. This can be achieved by shadowing this function for the duration of our test.

Unfortunately, shadowing a function in Common Lisp isn’t obvious.

You can’t use FLET or LABELS because they have lexical scope.
You can’t use DEFUN either because it affects the global function namespace and doesn’t let you save or restore the old definition.

The only way I know of is using the function (SETF FDEFINITION):

(let ((orig (fdefinition 'get-universal-time)))
  (setf (fdefinition 'get-universal-time) (lambda () *my-testing-time*))
  (prog1
    (overdue-p) ; you'd run some test checks against the result here
    (setf (fdefinition 'bar) orig)))

Wrapping this in a macro is left as an exercise to the reader.

This doesn’t work for special operators, and neither for FLET or LABELS. See the CLHS entry for accessor FDEFINITION.

Comments

  1. Zach Beane
    July 1st, 2008 | 3:08 pm

    This is undefined behavior per 11.1.2.1.2.

  2. foo
    July 1st, 2008 | 3:13 pm

    Well, ANSI Common Lisp does NOT allow to do that: changing definitions of the CL package. Many implementations will throw an error, because the package “CL” is locked.

    Plus in compiled code the Common Lisp function might be inlined, so a redefinition would not have an effect. The Common Lisp file compiler can for example assume that within a file the definitions don’t change. It can also assume that the functions of the Common Lisp package don’t change.

    So, what you describe may work in some implementation and may be useful there, in general it is not portable Common Lisp.

  3. July 1st, 2008 | 3:15 pm

    That’s good to know, thanks.

    So reliance on this specific behaviour — if used at all — should be preceded by a check for it.

  4. Richard Kreuter
    July 1st, 2008 | 4:12 pm

    Strictly speaking, when the standard says that the consequences of an action are undefined, the implementation is permitted to do anything at all when a program tries it: silently ignore the attempt, ring the system bell, crash, blow up the universe, etc.; nor does the implementation have to do the same things for each attempt, or document what it’s to do, etc. So a conforming program can’t programmatically detect the consequences of actions whose consequences the standard and the implementation don’t define.

  5. Tomas Zellerin
    July 1st, 2008 | 4:19 pm

    Why you do not use shadow for shadowing – function with same name in used package, calls COMMON-LISP counterpart, or – when testing – something other?

  6. Pascal Bourguignon
    July 1st, 2008 | 8:41 pm

    Indeed, there is nothing easier in Common Lisp than shadowing, be it a function or anything else named by a symbol (since CL:SHADOW applies to symbols).

    (defpackage “TEST-APPOINTMENT”
    (:use “CL”)
    (:shadow “GET-UNIVERSAL-TIME”))
    (in-package TEST-APPOINTMENT”)
    (defun get-universal-time () (random 1e12))
    (load “appointment.lisp”)

Leave a reply