2

I'm following Paul Graham's book On Lisp, where Section 5.7 says "A sharp-quoted lambda-expression is a constant, but a call to a constructor function will be evaluated at run time."

This reminds me of Lisp's general principle of code-data duality.

Can someone please help explain how a function is a constant when represented in computer memory? Sure it's just a set of instructions, but the functions still take inputs, so doesn't that mean the actual execution during run-time isn't constant?

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
aurbital
  • 121
  • 8

2 Answers2

2

What is meant is that a function is a constant object which cannot be modified. Like a shovel: you cannot change the shovel, but you can dig many different ditches with it. Similarly, you cannot change the function, but you can apply it to different arguments and get different values.

Incidentally, this is not necessarily always the case for all implementations. E.g., in CLISP:

(defun f(x) (+ x 10))
(compile 'f)
(f 5)
==> 15
(disassemble #'f)

Disassembly of function F
(CONST 0) = 10
1 required argument
0 optional arguments
No rest parameter
No keyword parameters
4 byte-code instructions:
0     (CONST&PUSH 0)                      ; 10
1     (LOAD&PUSH 2)
2     (CALLSR 2 55)                       ; +
5     (SKIP&RET 2)

Pretty straightforward, huh?

now, if you use internal functionality of CLISP to modify its vector of constants and replace 10 with something else, you will change the function's behavior:

(setf (sys::closure-const #'f 0) 42)
(f 7)
==> 49

This is similar to running a C program under a debugger and modifying local variables.

See also Why does an elisp local variable keep its value in this case? and Why does this function return a different value every time?

PS. Please note that doing this may lead to hard crashes (segfaults). Beware.

sds
  • 58,617
  • 29
  • 161
  • 278
  • so basically a function is a constant once translated into assembly code? – aurbital Nov 06 '20 at 16:58
  • yes, it should be treated as such. however, there are underhanded ways to defeat that, and one should not do those things. – sds Nov 06 '20 at 16:59
2

Paul Graham talks of constructors as functions which return new function objects. Not just simple function objects, but closures -> a function object and a binding environment. Thus a closure might be allocated at runtime, when a constructor call is evaluated at runtime. In his example he also shows that closures can sometimes also be allocated at read time - though that may have problems like compiling files and dumping function objects - which does not work.

Simple lambda expressions are pre-computed when compiled and thus may not see additional allocation at runtime to exist.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • I've yet to find a satisfactory (for my current understanding) definition of closure -- can you please help explain it? I know closures get created when free variables are involved. But are closures themselves *functions*? Or *environments*? Or *data*? – aurbital Nov 08 '20 at 14:24
  • a closure is a function object. So you can call it as a function. For example via FUNCALL or APPLY. The closure remembers the state of the functions lexically bound variables. `(funcall (let ((a 10)) (lambda (x) (+ a x))) 32)` -> 42 The lambda form in this example evaluates to a closure. – Rainer Joswig Nov 08 '20 at 19:30
  • 1
    !!! That explains Paul Graham's On Lisp Figure 6.5 page 79. I've been chewing on that one for days! Thank you! – aurbital Nov 09 '20 at 17:16