| > If I gave you a function that accepted Java source code as text and interpreted it, how would you detect that I was first converting it to Java bytecode and then interpreting that bytecode? > You couldn't, so it's an irrelevant internal detail. The function interprets Java source code. In a Lisp interpreter the actual source code is Lisp data, not text. One can give the function access to its source code, allow it to inspect it or even to change it - while the interpreter is executing this source code. In a Lisp interpreter, the code can even modify the source code (we are not talking about the byte-code or machine code) while it is running. CL-USER 20 > (let ((code (copy-tree '(+ 1 2 bar))))
`(defun foo (bar)
(print ,code)
(unless (eq (first ',code) '-)
(setf (first ',code) '-)
(foo bar))
(values)))
(DEFUN FOO (BAR) (PRINT (+ 1 2 BAR)) (UNLESS (EQ (FIRST (QUOTE (+ 1 2 BAR))) (QUOTE -)) (SETF (FIRST (QUOTE (+ 1 2 BAR))) (QUOTE -)) (FOO BAR)) (VALUES))
CL-USER 21 > (eval *)
FOO
CL-USER 22 > (foo 41)
44
-42
As you can see the function modified itself so that the operator + was replaced with a - and then called itself again with the same argument.If we now look at the function, we can see that it was indeed changing itself: CL-USER 23 > (pprint (function-lambda-expression #'foo))
(LAMBDA (BAR)
(DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 41B04012D3>))
(DECLARE (LAMBDA-NAME FOO))
(PRINT (- 1 2 BAR))
(UNLESS (EQ (FIRST '(- 1 2 BAR)) '-) (SETF (FIRST '(- 1 2 BAR)) '-) (FOO BAR))
(VALUES))
The PRINTED expression value is now computed with the - operator.The interpreted execution itself allows also different things then the compiled execution. For example if we have a break point, we can see the actual source code currently executed and we can change it while in the break point and then resume execution. Since the interpreter runs the source code, we can also can modify the interpreter to do something else with the source code, while it is executing it - like recording it or tracing it or stepping it. |