From ... Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!news.tele.dk!small.news.tele.dk!195.54.122.107!newsfeed1.bredband.com!bredband!uio.no!nntp.uio.no!ifi.uio.no!not-for-mail From: Erik Naggum Newsgroups: comp.lang.lisp Subject: Re: Creating functions at runtime Date: 06 Oct 2002 01:10:27 +0000 Organization: Naggum Software, Oslo, Norway Lines: 94 Message-ID: <3242855427179406@naggum.no> References: <87lm5cpubf.fsf@pokey.henrik-motakef.de> Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit X-Trace: maud.ifi.uio.no 1033866628 20327 129.240.65.5 (6 Oct 2002 01:10:28 GMT) X-Complaints-To: abuse@ifi.uio.no NNTP-Posting-Date: 6 Oct 2002 01:10:28 GMT Mail-Copies-To: never User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2 Xref: archiver1.google.com comp.lang.lisp:43162 * Henrik Motakef | Trying to learn about the "Code as Data" feature of CL, I'd like to have | some feedback if I'm on the right track or already misusing things. There are at least two different ways to look at this. The first is to realize that a compiler will expect to read code and then examine the mechanisms by which the compiler reads code. Common Lisp has the function `read´ which is used by the compiler to read the code. By making this an ordinary part of the language and exporting it to the environment that ordinary users can use, too, programs that need to work on code can use it to read code and be able to work with it without having to duplicate the work the compiler needs to do to read code. You realize that this in sharp contrast to most other programming languages, which generally do not contain neither functionality nor the system types to deal with source code. This leads us to the second way to look at code is data. The fundamental data type for source code is the list, and this is the same list as user programs work with. (Some therefore think that the only data type in Lisp is the list, but even though we do not represent source code with vectors or strings, these types are just as fundamental to the programmers as the list. Just wanted to clear that thing up preventatively.) Since the functions to manipulate the list are ordinary parts of the languages and exported to the user environment like `read´, there is some functionality waiting to happen like an emergent property of the language: functions called by the compiler to operate on the code being compiled. Languages without `read´ invent some complex rewriting systems and a lot of theory to go with them, but Common Lisp offers the programmer the /macro/, which processes an expression in the language as data, but it really is code and momentarily be turned into machine code by the compiler. This leads to yet another way to look at "code is data", because functions are also ordinary language objects and may be passed around at will. This is usually referred to as first-order functions and not usually as "code is data". Then there is `eval´, of course, but I personally think that this is not the regular meaning of "code is data", either. Common Lisp would have "code is data" even if you did not have `eval´ or the compiler available at run-time. | Pretending *names* might be huge and testing whether an argument is nil | might be expensive, I don't want to do the test for every name, so the | general idea is to first build some function based on the passed | arguments that returns whether a given name matches, and use this to | filter the list of names, like this: Although testing for `nil´ is among the fastest things a Common Lisp program can do, it will probably slow things down a bit, so this is not an unreasonable plan. However, actually constructing the code at runtime is not a good idea in this particular case. Much better techniques are available. For instance, while you have seen that you can create a /predicate/ according to the parameters, there are other options. (defun get-matching-name (first-name last-name) (cond ((and first-name last-name) ;; if multiple matches for one full name are possible, use ;; (remove (list first-name last-name) *names* :test-not #'equal) (find (list first-name last-name) *names* :test #'equal)) (first-name (remove first-name *names* :test #'string/= :key #'first)) (last-name (remove last-name *names* :test #'string/= :key #'second)) (t *names*))) It really would be useful to agree on a name for the opposite of `remove´/`delete´, like perhaps `collect´/`retain´. | While this might be not optimal [...], it doesn't look too bad, IMHO. I think run-time construction of functions like this should be done when you have an input language that is more complex to handle than the code that would process it as an interpreted mini-language. | One thing that still bugs me is the use of EVAL. Note that (compile nil '(lambda ...)) produces a compiled function. | Given that EVAL doesn't seem to have a better reputation among CLers than | it's equivalents in other languages, I wonder: is there a better way to | get a function from a list built at runtime? `eval´ is perfectly acceptable when the purpose is to evaluate user code or expressions from an input language. As a general principle: If the code you evaluate does not come from a run-time source, you should not perform run-time evaluation of it, either. [ If you should dislike the tone or any part or aspect of the contents of this message so much that you feel compelled to attack or denounce me instead of staying focuesd on the questions you asked and the purpose you had when you asked them, please do not post your hostility. ] -- Erik Naggum, Oslo, Norway Act from reason, and failure makes you rethink and study harder. Act from faith, and failure makes you blame someone and push harder.