2

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.

Srv19
  • 3,458
  • 6
  • 44
  • 75
  • 1
    what's the actual problem you have with this task? What did you try? – Rainer Joswig Dec 20 '15 at 22:08
  • Actual problem i have is: naive approach fails to work. That is, i get lambda containing free symbol which consequently fails to work – Srv19 Dec 21 '15 at 08:41
  • So this is in fact an Emacs Lisp question? I'll update the answer – coredump Dec 21 '15 at 08:51
  • 1
    Yes, I should have tagged is as such - common-lisp tag was added by someone else. – Srv19 Dec 21 '15 at 08:57
  • Does the answer to [Emacs lisp: why does this sexp cause an invalid-function error?](http://stackoverflow.com/q/17102051/1281433) help here? Ultimately it comes down to the same thing: emacs's dynamic scoping. And I think that the answer there, using emacs's `lexical-let` might be a better solution that constructing a function with an injected constant value, at least in some contexts. – Joshua Taylor Dec 21 '15 at 15:09
  • I did want a constant there, though. Thanks for mentioning lexical-let, it will come in handy as well – Srv19 Dec 21 '15 at 16:15

2 Answers2

7

Emacs

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 

Common Lisp

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
coredump
  • 37,664
  • 5
  • 43
  • 77
  • I must ask you for a reference on the syntax you have used. This seems to be just what i needed yet i never saw it in elisp manual – Srv19 Dec 21 '15 at 10:14
  • 1
    @Srv19 backquotes: https://www.gnu.org/software/emacs/manual/html_node/elisp/Backquote.html. And [this question](https://stackoverflow.com/questions/593383/how-do-i-do-closures-in-emacs-lisp) and its answers are quite informative. – coredump Dec 21 '15 at 10:20
  • Is the backquote solution better than using `lexical-let` to capture the variable? Since it's "read-only" in this case, I guess it'd be the same, but this weren't just an adder, but an accumulator or something, you'd *want* a closure over a binding, not a anonymous function with an injected constant value. See, e.g., [this answer](http://stackoverflow.com/a/17108245/1281433) (disclaimer, it's mine) for an example. (Actually, that's the answer in which I first learned about emacs's lexical-let.) – Joshua Taylor Dec 21 '15 at 15:11
  • @JoshuaTaylor You are right, I should have mentioned lexical-let. Somehow the answer evolved from being CL-only to Emacs+CL, and I am not really satisfied with it. I'll edit later when I have more time. – coredump Dec 21 '15 at 15:19
  • Well, in my use case function will be called exactly once but at undetermined moment in time, so there is no reason to capture a variable – Srv19 Dec 21 '15 at 16:12
2

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.

Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353