Reputation: 3608
I need to make a function that generates functions like this:
(defun add3 (val) (+ 3 val))
It should be usable like this:
(setq adder7 nil)
(fset 'adder7 (make-adder 7))
(adder7 3)
=> 10
Ideally, function make-adder should return lambdas that do not have any symbols inside other then their parameter, for example:
(make-adder 7)
=> (lambda (val) (+ 7 val))
Update
I have tried following naive realisation:
(defun make-adder (n)
(lambda (x) (+ n x)))
But that generates a lambdda that contains free symbol (not numeric constant!) and its usage fails.
(defalias 'add1 (make-adder 1))
(add1 2)
=> Debugger error void-variable n
(let ((n 5))
(add1 2))
=> 7
Which is not what i want to get at all.
Upvotes: 2
Views: 388
Reputation: 85823
coredump-'s answer address the right issue: emacs's dynamic scoping means that you no longer have access to the value of n that was in scope when your adder function was called. Rather than using backquote to construct a function with the constant injected, though, I think that it might be more descriptive to use emacs's lexical-let, since what you're looking for is a lexical closure. Just to be clear, here's the behavior you're getting now, with dynamic scoping:
(defun dyn-adder (n)
(lambda (x)
(+ n x)))
(funcall (dyn-adder 3) 5)
;; (void-variable n) error...
And here's how you can use lexical-let to get an actual lexical closure:
(defun lex-adder (n)
(lexical-let ((n n))
(lambda (x)
(+ n x))))
(funcall (adder 3) 5)
;; 8
There isn't much difference between the backquote solution and lexical-let in defining a simple adder, but there are contexts where it's important to actually have a variable that you're referencing. E.g., if you wanted to create an accumulator, you'd need that local state. With dynamic scoping you'd get the same void-variable error:
(defun dyn-accumulator (n)
(lambda (x)
(incf n x)))
(let ((acc (dyn-accumulator 5)))
(funcall acc 3)
(funcall acc 8))
;; (void-variable n) error...
With a backquote approach, you'll get a different error. (I'm not sure I'm doing this correctly, though. I think that the error I'm getting is from trying to funcall a list, not from the function having a constant in it. But in any case, it should be clear that (lambda (x) (incf 5 x)) won't work, because you can't increment the constant value.)
(defun bq-accumulator (n)
`(lambda (x)
(incf ,n x)))
(let ((acc (bq-accumulator 5)))
(funcall acc 3)
(funcall acc 8))
;; wrong type argument error...
But with lexical-let, we have a real variable that we can modify:
(defun lex-accumulator (n)
(lexical-let ((n n))
(lambda (x)
(incf n x))))
(let ((acc (lex-accumulator 5)))
(funcall acc 3)
(funcall acc 8))
;; 16
I learned about and described lexical-let more in an answer I wrote for Emacs lisp: why does this sexp cause an invalid-function error?; that may be useful information here, as well.
Upvotes: 2
Reputation: 38799
Emacs relies on dynamic scoping by default. That's why the n
symbol inside the returned lambda refers to an unbound variable. Either you toggle lexical scoping or you build a lambda form with the current value of n
, as follows:
(defun make-adder (n)
`(lambda (x) (+ ,n x)))
And then:
(defalias 'add1 (make-adder 1))
(add1 3)
=> 4
I originally thought the question was about Common Lisp (fset should have given me a hint), where you only have to do this:
(defun make-adder (n)
(lambda (x) (+ n x)))
Your function takes a n
and returns an anonymous function, which takes another parameter x
and produces the result.
But wait, make-adder
is just a special case of partially applying some arguments to a function (see this question for details on the distinction between currying and partial application). The general approach to partially apply functions is:
(defun partial (function &rest partial-args)
(lambda (&rest args)
(apply function (append partial-args args))))
For example:
(let ((3+ (partial #'+ 3)))
(funcall 3+ 7))
=> 10
Upvotes: 7