let and flet in emacs lisp

Viewed 11081

I don't know if you would call it the canonical formulation, but to bind a local function I am advised by the GNU manual to use 'flet':

(defun adder-with-flet (x)
  (flet ( (f (x) (+ x 3)) )
    (f x))
)

However, by accident I tried (after having played in Scheme for a bit) the following expression, where I bind a lambda expression to a variable using 'let', and it also works if I pass the function to mapcar*:

(defun adder-with-let (x)
  (let ( (f (lambda (x) (+ x 3))) )
    (car (mapcar* f (list x)) ))
)

And both functions work:

(adder-with-flet 3)   ==> 6
(adder-with-let 3) ==> 6

Why does the second one work? I cannot find any documentation where 'let' can be used to bind functions to symbols.

4 Answers

Unlike Scheme, Emacs Lisp is a 2-lisp, which means that each symbol has two separate bindings: the value binding and the function binding. In a function call (a b c d), the first symbol (a) is looked up using a function binding, the rest (b c d) are looked up using the value binding. Special form let creates a new (local) value binding, flet creates a new function binding.

Note that whether value or function binding is used for lookup depends on the position in the (a b c d) function call, not on the type of the looked-up value. In particular, a value binding can resolve to function.

In your first example, you function-bind f (via flet), and then do a function lookup:

(f ...)

In your second example, you value-bind f to a function (via let), and then use a value lookup:

(... f ...)

Both work because you use the same kind of binding and lookup in each case.

http://en.wikipedia.org/wiki/Common_Lisp#Comparison_with_other_Lisps

I did a quick search of the Emacs lisp manual and couldn't find any reference to 'flet, which isn't terribly surprising since that is a part of cl - the common-lisp package.

let will do a local binding as well, but it won't bind to the "function cell" for that symbol.

i.e. This works:

(let ((myf (lambda (x) (list x x))))
  (eval (list myf 3)))

but

(let ((myf (lambda (x) (list x x))))
  (myf 3))

fails with the error: "Lisp error: (void-function myf)"

flet on the other hand, does do the binding to the function cell, so this works:

(flet ((myf (x) (list x x)))
  (myf 3))

Notice the difference being that flet allows you to use the symbol myf directly, whereas the let does not - you have to use some indirection to get the function out of the "value cell" and apply that appropriately.

In your example, the 'mapcar' did the equivalent to my use of 'eval.

Related