Reputation: 44892
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
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.)makenewfoo()
or something
like this so I can invoke
makenewfoo(foo)
and allow this to
create newfoo()
.And, more generally,
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?Upvotes: 4
Views: 968
Reputation: 14291
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)))
Upvotes: 4