I want to be able to read a lambda expression from the keyboard. For example, if a function square has already been DEFUNed I can enter the symbol name:
(defun square (x) (* x x))
so that when the following is evaluated:
(funcall (read) 2)
the user can type square
and the result is 4. But if the user types
(lambda (x) (* x x))
the result is an error, for example in Macintosh Common Lisp,
Error: (LAMBDA (X) (* X X)) can't be FUNCALLed or APPLYed
Is there an easy way to do this I am missing?
Thanks.
read
returns a list
which has to be evaluated before it can be funcall
ed.
This can be accomplished using read-time evaluation:
(funcall (read) 2)
#.(lambda (x) (* x x))
==> 4
However, generally speaking, this is a security hole (you are evaluating user-supplied code - what if they typed #.(start-nuclear-war)
?) so a cautious engineer will bind *read-eval*
to nil
when reading input they have no control over.
Thus it is much better to use coerce
explicitly:
(funcall (coerce (let ((*read-eval* nil)) (read)) 'function) 2)
1+
==> 3
(funcall (coerce (let ((*read-eval* nil)) (read)) 'function) 2)
(lambda (x) (* x x))
==> 4
Since you are using read
, in general you would need to evaluate the returned forms to obtain meaningful values. But in your particular case, you can use COERCE
. For example, from a REPL:
CL-USER> (coerce '+ 'function)
#<FUNCTION +>
The above finds the function to which the symbol +
is fbound.
CL-USER> (coerce '(lambda (x) (* x x)) 'function)
#<FUNCTION (LAMBDA (X)) {53F2BF2B}>
The above takes a lambda expression and turn it into a function object.
You get that error because READ
returns just the list (LAMBDA (X) (* x x))
, it does not evaluate it to a function. To do that, you would need to write:
(funcall (eval (read)) 2)
Note though that in that case, just writing square
doesn't work anymore, the user would now need to enter #'square
.
CL-USER 8 > (defun read-function (&optional (stream *standard-input*))
(let ((f (read stream)))
(cond (; function object
(functionp f) f)
(; symbol naming a function
(symbolp f) (symbol-function f))
(; (function f)
(and (consp f)
(eq (first f) 'function))
(eval f))
(; (lambda ...)
(and (consp f)
(eq (first f) 'lambda))
(eval f)))))
READ-FUNCTION
Examples:
CL-USER 9 > (read-function)
#.#'+
#<Function + 40F0044AD4>
CL-USER 10 > (read-function)
+
#<Function + 40F0044AD4>
CL-USER 11 > (read-function)
#'+
#<Function + 40F0044AD4>
CL-USER 12 > (read-function)
(lambda (a b) (+ a b))
#<anonymous interpreted function 4060000C8C>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With