4

So I thought one of the advantages of lisp (among other languages) is its ability to implement function factories (accept functions as arguments; return new functions). I want to use this capability to make small changes to a function and save it as a new function so that if changes are made to the original function, they are also reflected in the new function on which it is based. Note: I am not the one writing the original function so I can't necessarily encapsulate the common parts in a separate function to be called by both, which would be the obvious answer otherwise.

Toy example in emacs lisp (may not be the most ideal as it is a lisp-2):

I have a function, foo that is provided to me:

(defun foo (x y)
    (+ x y)))

I want my new function to include a statement that allows me to change the value of a variable if a certain condition is met. For instance:

(defun newfoo (x y)
  (if (condition-met-p x) 
      (setq x (transform x)))
    (+ x y))

Please disregard that I could use defadvice in this particular example as I am more interested in the general task of modifying functions where defadvice may not apply. I believe I can modify the body with this form:

(setq conditional-transformation 
      '(if (condition-met x) (setq x (transform x))))

(setq newbody (append conditional-transformation 
              (nth 2 (symbol-function 'foo)))))

My questions are specifically how to

  1. create a copy of foo to newfoo and replace the body with the value of newbody defined above. (I've looked into fset, setf, and function but perhaps not using them properly.)
  2. possibly wrap this in a function called makenewfoo() or something like this so I can invoke makenewfoo(foo) and allow this to create newfoo().

And, more generally,

  1. is something like this is commonly done or there is a more idiomatic way to modify functions?
  2. this is a very simple case, but is there a more general way than specifying the list element number to nth for the modification. For instance, the actual function is more complex so is there a way to recursively search down this s-expression tree and test for a particular syntax and insert this conditional-transformation expression before or after it (possibly using equal), so it is less sensitive to changes made in the original function?

1 Answer 1

4

It does work in Emacs Lisp:

elisp> (defun foo (x y)
         (+ x y))
foo
elisp> (fset 'newfoo
             (append (lambda (x y)
                       (when (< x 2)
                         (setq x (* x 2))))
                     (cddr (symbol-function 'foo))))
(lambda
  (x y)
  (when
      (< x 2)
    (setq x
          (* x 2)))
  (+ x y))

elisp> (newfoo 1 3)
5
elisp> (newfoo 3 3)
6

But I really don't think that it is commonly done or idiomatic. You should use defadvice if you want to modify the behavior of functions.

As far as CL is concerned: Some implementations provide similar functions/macros (for example in CCL: ccl:advise), and you can specify :before, :after, and :around methods for generic functions.


Example code for insertion of expressions:

(defun find-node (elt tree)
  (cond ((null tree) nil)
        ((equal (car tree) elt) tree)
        ((consp (car tree)) (let ((node (find-node elt (car tree))))
                              (if node node (find-node elt (cdr tree)))))
        (t (find-node elt (cdr tree)))))

(defun insert-before (node elt)
  (setcdr node (cons (car node) (cdr node)))
  (setcar node elt))

(let* ((function (copy-tree (symbol-function 'foo)))
       (node (find-node '(+ x y) function)))
  (when node
    (insert-before node '(if (< x 2) (setq x (* x 2))))
    (fset 'newfoo function)))
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! Yes, needed the lambda... but I thought part of the "code as data" philosophy was that you can pick apart and manipulate your functions as you do with your data. Because there is no defadvice or advise :within[deeply nested leaf] AFAIK ;)...
Well, I think there may be a difference between "code as data" and "already evaluated function definitions as data", especially when compiled. For example, in CL, there is no standard way to get back the code of an already evaluated function definition. There is function-lambda-expression, but implementations are allowed to return "nil, true, nil". Even if they provide this functionality, the output will differ between implementations. Also, note that Emacs Lisp's defadvice is still very powerful, and most use cases can be solved with an around advice.
I see... and so if I wanted to do this for uncompiled functions in general -- to make several functions from a common s-expression, for instance, there wouldn't be any tools to help me recursively traverse through branches of the s-expression to match and append elements? I do use defadvice at times but my main issue that I should have perhaps emphasized is that I want to be able to "insert" or "remove" small expressions as I might with pieces of data.
I am not an Emacs Lisp expert, so I don't know if such a functionality is "built in". If it is, then I don't know about it. But it wouldn't be that hard to write a little function which searches a given tree and inserts code before/after a match. Alternatively, there may be code walkers that allow doing this.
@danlei: Thank you so much -- this has been incredibly helpful. So this is not so common but also possible the the definition of a few relatively simple functions (I was expecting that something like find-node would be part of any type of lisp...).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.