Reputation: 1941
In SBCL, how does one put a lambda expression into a structure slot [eg, (setf (struct-slot1 struct1) '(lambda (x) (* x x)))
], so that it can be called with funcall
or apply
? The SBCL compiler complains with: wanted one of (FUNCTION SYMBOL)
. Is there a way to install such a lambda without compiling it or giving it an explicit name? Why is a lambda expression not a function? I'd like to do something like: (funcall (function (struct-slot1 struct1)) 3)
. Thanks for any insights. (ps: normally I compile the lambda before running, but during debugging I need to see the innards of the lambda.)
Upvotes: 1
Views: 908
Reputation: 139261
This is not SBCL specific, but according to the ANSI Common Lisp standard.
Convert a Lambda Expression as List to a Function
These are your options:
CL-USER 168 > (funcall (coerce '(lambda (x) (* x x))
'function)
4)
16
CL-USER 169 > (funcall (compile nil '(lambda (x) (* x x)))
4)
16
CL-USER 170 > (funcall (eval '(lambda (x) (* x x))) ; because LAMBDA is a macro, too
4)
16
CL-USER 171 > (funcall (eval '(function (lambda (x) (* x x))))
4)
16
Note though that the lambda expression is referencing the null lexical environment. Thus it has no access to any lexical variables from the surrounding code.
Lambda expressions are not function objects
Why is a lambda expression not a function?
Because it is just a list, not code. To make a lambda expression into code, you have to turn it into a function object.
Some other (often older) Lisps allow you to use lambda expressions as code, but not Common Lisp. This is defined by the standard.
You can't do (funcall '(lambda (x) (+ x x)) 3)
. You have to convert the lambda expression into a function object first.
FUNCTION is a special operator -> syntax+semantics
(funcall (function (struct-slot1 struct1)) 3)
This is a syntax error, already. FUNCTION
is a special operator and expects a function name or a lambda expression. (struct-slot1 struct1)
is neither. (struct-slot1 struct1)
is code which retrieves a value from a structure. But it is not a function name, which would be a symbol or a list (setf <some-symbol>)
. It is also not a lambda expression, which would be something like (lambda (...) ...)
.
Upvotes: 1
Reputation: 38809
(ps: normally I compile the lambda before running, but during debugging I need to see the innards of the lambda.)
Try this:
(proclaim '(optimize (debug 3)))
In SBCL, you can also do this:
(sb-ext:restrict-compiler-policy 'debug 3)
... which establishes the lowest allowed policy for debugging. The documentation says:
See also :POLICY option in WITH-COMPILATION-UNIT.
Now, define a simple hash table for tests:
CL-USER> (defparameter *hash* (make-hash-table :test #'eq))
Insert an anonymous function:
CL-USER> (setf (gethash :fun *hash*) (lambda (u) (* 10 u)))
#<FUNCTION (LAMBDA (U)) {1005140E2B}>
Call it badly:
CL-USER> (funcall (gethash :fun *hash*) "oops")
The debugger shows up:
Argument X is not a NUMBER: "oops"
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [RETRY] Retry SLIME REPL evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {10041C8033}>)
Backtrace:
0: (SB-KERNEL:TWO-ARG-* "oops" 10)
1: ((LAMBDA (U)) "oops")
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FUNCALL (GETHASH :FUN *HASH*) "oops") #<NULL-LEXENV>)
3: (EVAL (FUNCALL (GETHASH :FUN *HASH*) "oops"))
Go to frame #1, ((LAMBDA (U)) "oops")
, and press v (a.k.a. sldb-show-source
). A new buffer pops up with the following content:
(LAMBDA ()
(PROGN
(LET* ((#:HASHTABLE *HASH*) (#:NEW1 (LAMBDA (U) (#:***HERE*** (* 10 U)))))
(SB-KERNEL:%PUTHASH :FUN #:HASHTABLE #:NEW1))))
Thanks to the debugger policy being high enough, the anonymous function's source code is available, even though it was typed in the REPL. If your anonymous function happens to be compiled from a source file, then pressing v will go to the appropriate point in that file (which corresponds to the form wrapped in a ***HERE***
term above).
If you want, you can also store the code as data before you compile it:
(defun wrap-and-compile (code)
(let ((function (compile nil code)))
(compile nil `(lambda (&rest args)
(restart-case (apply ,function args)
(DEBUG () :report ,(format nil "~S" code)
',code))))))
(setf (gethash :wrapped *hash*)
(wrap-and-compile '(lambda (x) (* 10 x))))
(funcall (gethash :wrapped *hash*) "Boo")
And the debugger shows:
Argument X is not a NUMBER: "Boo"
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [DEBUG] (LAMBDA (X) (* 10 X))
1: [RETRY] Retry SLIME interactive evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#<THREAD "worker" RUNNING {100513A403}>)
This is a little bit hacky, but it works. The DEBUG restarts returns the original data from which the function was compiled.
Upvotes: 1