Now, we’re going to switch a little bit, because I’m discussing flet. Unlike many of the commands I’ve been talking about, the beginner Lisp programmer is quite likely to have encountered flet in introductory material. Depending on the source, though, the distinction between flet and labels may not have been made particularly clear, so that’s what I’ll be talking about here.
The flet special operator, like labels, modifies the lexical environment, and like labels it does not intern a new symbol. The difference is that the forms in the flet do not, themselves, see those modifications to the lexical environment. For purposes of writing the forms within flet, symbols are resolved according to the enclosing environment. This means, for instance, that flet-defined functions cannot call themselves recursively. This is the difference between flet and labels. Examples may help.
In the code below, I write a pair of global functions, func-A and func-B. If func-A is called, it recursively calls func-A, then calls func-B. Output appears on the screen to show what functions are being called. I then shadow these functions with forms in flet, in demonstrate-flet, and labels, in demonstrate-labels. The new definitions are exactly the same as the global ones, save for the text that is printed, to demonstrate which version of the function is being called. As you can see, under the flet, the invocation of func-A calls the one defined in the flet, but within the flet, the recursive call to func-A calls, instead, the global symbol function. On the other hand, under labels, the recursive call to func-A calls itself, the definition in the labels statement.
(defun func-A (&optional (depth 0))
(format t "~V,0TThis is depth ~D of global symbol table func-A~%"
(* depth 10) depth)
(when (= 0 depth)
(format t "Calling the functions func-A and func-B...~%")
(func-A (1+ depth))
(func-B (1+ depth))))
(defun func-B (&optional (depth 0))
(format t "~V,0TThis is depth ~D of global symbol table func-B~%"
(* depth 10) depth))
(defun demonstrate-flet ()
(flet
((func-A (&optional (depth 0))
(format t "~V,0TThis is depth ~D of flet func-A~%"
(* depth 10) depth)
(when (= 0 depth)
(format t "Calling the functions func-A and func-B...~%")
(func-A (1+ depth))
(func-B (1+ depth))))
(func-B (&optional (depth 0))
(format t "~V,0TThis is depth ~D of flet func-B~%"
(* depth 10) depth)))
(func-A)))
(defun demonstrate-labels ()
(labels
((func-A (&optional (depth 0))
(format t "~V,0TThis is depth ~D of labels func-A~%"
(* depth 10) depth)
(when (= 0 depth)
(format t "Calling the functions func-A and func-B...~%")
(func-A (1+ depth))
(func-B (1+ depth))))
(func-B (&optional (depth 0))
(format t "~V,0TThis is depth ~D of labels func-B~%"
(* depth 10) depth)))
(func-A)))
Output:
CL-USER> (demonstrate-flet)
This is depth 0 of flet func-A
Calling the functions func-A and func-B...
This is depth 1 of global symbol table func-A
This is depth 1 of global symbol table func-B
NIL
CL-USER> (demonstrate-labels)
This is depth 0 of labels func-A
Calling the functions func-A and func-B...
This is depth 1 of labels func-A
This is depth 1 of labels func-B
NIL