2

Here is something you can do in Scheme:

> (define (sum lst acc)
    (if (null? lst)
        acc
        (sum (cdr lst) (+ acc (car lst)))))
> (define sum-original sum)
> (define (sum-debug lst acc)
    (print lst)
    (print acc)
    (sum-original lst acc))
> (sum '(1 2 3) 0)
6
> (set! sum sum-debug)
> (sum '(1 2 3) 0)
(1 2 3)
0
(2 3)
1
(3)
3
()
6
6
> (set! sum sum-original)
> (sum '(1 2 3) 0)
6

If I were to do the following in Common Lisp:

> (defun sum (lst acc)
    (if lst
        (sum (cdr lst) (+ acc (car lst)))
        acc))
SUM
> (defvar sum-original #'sum)
SUM-ORIGINAL
> (defun sum-debug (lst acc)
    (print lst)
    (print acc)
    (funcall sum-original lst acc))
SUM-DEBUG
> (sum '(1 2 3) 0)
6

Now how can I do something like (setf sum #'sum-debug) that would change the binding of a function defined with defun?

Matt
  • 21,026
  • 18
  • 63
  • 115
  • 1
    While the questions may not (or may) be duplicates , you might find an answer to you question in the answers to some other questions, like [How to store a function in a variable in Lisp and use it](http://stackoverflow.com/q/19375270/1281433), [LISP dynamically define functions](http://stackoverflow.com/q/22975732/1281433), and [why defun is not the same as (setq )?](http://stackoverflow.com/q/11212717/1281433) – Joshua Taylor Mar 02 '15 at 17:53

2 Answers2

7

Because Common Lisp has a different namespace for functions, you need to use symbol-function or fdefinition.

CL-USER> (defun foo (a)
           (+ 2 a))
FOO
CL-USER> (defun debug-foo (a)
           (format t " DEBUGGING FOO: ~a" a)
           (+ 2 a))
DEBUG-FOO
CL-USER> (defun debug-foo-again (a)
           (format t " DEBUGGING ANOTHER FOO: ~a" a)
           (+ 2 a))
DEBUG-FOO-AGAIN
CL-USER> (foo 4)
6
CL-USER> (setf (symbol-function 'foo) #'debug-foo)
#<FUNCTION DEBUG-FOO>
CL-USER> (foo 4)
 DEBUGGING FOO: 4
6
CL-USER> (setf (fdefinition 'foo) #'debug-foo-again)
#<FUNCTION DEBUG-FOO-AGAIN>
CL-USER> (foo 4)
 DEBUGGING ANOTHER FOO: 4
6
CL-USER>
Inaimathi
  • 13,853
  • 9
  • 49
  • 93
  • 1
    [**(setf fdefinition)**](http://www.lispworks.com/documentation/HyperSpec/Body/f_fdefin.htm) can be a little bit more flexible than **(setf fdefinition)** since it works with arbitrary function names, not just symbols. – Joshua Taylor Mar 02 '15 at 17:52
  • @JoshuaTaylor: What else can be a function name other than a symbol? – Matt Mar 02 '15 at 18:41
  • 4
    @Matt a list of the form **(setf something)**. you can, e.g., **(defun (setf kar) (value kons) ...)** – Joshua Taylor Mar 02 '15 at 18:52
  • 5
    Note that this works in many cases. Exception: when compiled file, the compiler may inline function calls. Then a new definition will not be seen, unless the calling code is recompiled. – Rainer Joswig Mar 02 '15 at 19:26
5

A typical use case for redefining functions in such a way is during debugging. Your example indicates that. Common Lisp already provides higher-level machinery for that: TRACE. Under the hood it sets the function value of the symbol, but it provides a more convenient user interface. Often something like TRACE will have many, non-standard, options.

Tracing functions

The following example uses Clozure Common Lisp, which is always compiling code:

? (defun sum (list accumulator)
    (declare (optimize debug))   ; note the debug declaration
    (if (endp list)
        accumulator
        (sum (rest list) (+ accumulator (first list)))))
SUM
? (sum '(1 2 3 4) 0)
10
? (trace sum)
NIL
? (sum '(1 2 3 4) 0)
0> Calling (SUM (1 2 3 4) 0) 
 1> Calling (SUM (2 3 4) 1) 
  2> Calling (SUM (3 4) 3) 
   3> Calling (SUM (4) 6) 
    4> Calling (SUM NIL 10) 
    <4 SUM returned 10
   <3 SUM returned 10
  <2 SUM returned 10
 <1 SUM returned 10
<0 SUM returned 10
10

Then to untrace:

? (untrace sum)

Advising functions

In your example you have printed the arguments on entering the function. In many Common Lisp implementations there is another mechanism to augment functions with added functionality: advising. Again using Clozure Common Lisp and its advise macro:

? (advise sum                                 ; the name of the function
          (format t "~%Arglist: ~a" arglist)  ; the code
          :when :before)                      ; where to add the code
#<Compiled-function (CCL::ADVISED 'SUM) (Non-Global)  #x302000D7AC6F>

? (sum '(1 2 3 4) 0)

Arglist: ((1 2 3 4) 0)
Arglist: ((2 3 4) 1)
Arglist: ((3 4) 3)
Arglist: ((4) 6)
Arglist: (NIL 10)
10
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • Wow, thanks for the very useful tips! Side question: What's the point of `endp` if it is the same as `not` (or reordering the `if` expression)? – Matt Mar 02 '15 at 20:10
  • 3
    @Matt: `ENDP` indicates that the argument is a list. `(endp somelist)` vs. `(null anything)`. Makes reading a code a little bit better and may also signal an error when `ENDP` is called with something other than a list. – Rainer Joswig Mar 02 '15 at 20:15