Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatical Function Definition: How to get rid of "eval" here?

Tags:

common-lisp

I have a set of functions named "ip", "date", "url" etc.

With these, I want to generate another set of functions "ip-is", "date-is" etc.

I finally have the following solution, thats working fine, but that uses "eval".

(loop for name in '(ip date url code bytes referer user-agent) do
  (let ((c-name (intern (concatenate 'string (symbol-name name) "-IS"))))
    (eval `(defun ,c-name (c)
           #'(lambda (l) (equal (,name l) c))))))

Can someone help me, how to get rid of the "evil eval"? It is essential for my program that the function names are provided as a list. So a call to some marcro

   (define-predicate ip)
   (define-predicate date)
   (define-predicate url)

etc.

would not fit my needs. I have no real problem with "eval", but I read very often, that eval is considered bad style and should be avoided if possible.

Thanks in Advance!

like image 887
Patrick Avatar asked Feb 10 '13 14:02

Patrick


2 Answers

You should use a macro here. Macros are evaluated during compile (or load) and can be used to programatically generate a function definition. Your code could be written something like this:

(defmacro define-predicates (&rest names)
  `(progn
     ,@(loop
          for name in names
          collect (let ((c-sym (gensym))
                        (l-sym (gensym)))
                    `(defun ,(intern (concatenate 'string (symbol-name name) "-IS")) (,c-sym)
                       #'(lambda (,l-sym) (equal (,name ,l-sym) ,c-sym)))))))


(define-predicates ip date url)

Note that the symbols are generated using GENSYM in the functions. In this particular case, that's not strictly necessary, but I usually prefer to do it this way just so that there is no chance of having any leaking if I were to refactor the code at a later stage.

like image 176
Elias Mårtenson Avatar answered Nov 05 '22 12:11

Elias Mårtenson


If you want to use a function (instead of a macro as in the other answer), you should be using (setf fdefinition):

(loop for name in '(ip date url code bytes referer user-agent) do
  (let ((c-name (intern (concatenate 'string (symbol-name name) "-IS"))))
    (setf (fdefinition c-name)
          (lambda (c) (lambda (l) (equal (funcall name l) c))))))
like image 31
sds Avatar answered Nov 05 '22 14:11

sds