I am learning Lisp. Now I am trying to create a function that takes some valid Lisp form as argument and returns a function that executes the Lisp forms when called. For example:
(defun fn (name action)
(setf (symbol-function name)
#'(lambda () action)))
When I am passing say (+ 4 5 6)
the function is getting created with specific name and when called returning the sum.
(fn 'add (+ 4 5 6))
(add) ==> 15
But if I invoke (fn 'error (assert (= 2 3))
it is throwing error (= 2 3) must evaluate to a non-NIL value.
and the function with name error
is not created.
How can I stop this evaluation of assert
when passed as function parameter?
This doesn't treat
add
and(+ 4 5 6)
in the same way. You quote one (because you want a symbol), but not the other, even though you want a list. To get the behavior you want, you'll either need to define a macro so that you can prevent evaluation and put the form inside a function, or to construct a list that you coerce to a function. The macro approach:The function and coerce approach:
Now, these approaches will work fine, but Rainer Joswig's answer points out that there's a standard function that already does most of this work for us: compile. It's pretty versatile, but the important parts are that it takes a name and optionally a function definition. The function definition can be a lambda expression, and it will be coerced to a function (as above), but also compiled (since the simple coercion above might not compile the function) and store it as the function definition of the name, if name is non-nil. That means that compile will do all the work of
for you, with the added benefit of compiling the function, too. Rainer's answer shows how it can be used, and I think it's the most elegant solution to this problem.
You cannot write this function; it has to be a macro operator. If
fn
is a function, then the call:evaluates the argument
(+ 4 5 6)
, reducing it to the value 15. The function receives 15, and not the expression. We can "fix" this by quoting the code:but then we have the problem that the code doesn't interact with the lexical environment. For instance, this won't work, because
x
is not visible insidefn
:To create a function which evaluates (+ x 2) in the proper environment, we must the
lambda
operator right in that same lexical scope:Your
fn
operator can be written as a syntactic sugar that generates thelambda
(without any name):Now we can write:
To do the named thing:
However, this is a quite a poor idea; we're introducing a nasty global side effect into a function. A better "named fn" might be one which introduces a lexical binding for a function over some forms. That is, it can be used like this:
That could be done like this:
Or, if you want the name as a variable binding rather than a function binding, so that the usage is
(fn (foo (+ x 2)) (funcall foo)):
Create the function, compile it and store it under
name
:Let's try it: