I understand that both are used to temporarily change the value of a function. Beyond the fact that cl-flet
is a function and cl-letf
is a macro, when do you use them?
The bindings can be recursive
If the function definition calls itself by name, which function will be called? (compare cl-flet
vs cl-labels
behaviour).
Scoping is lexical ... capturing them in closures ...
Read about lexical binding/scope vs dynamic binding/scope.
cl-letf
can be used to set dynamically-bound function values, by using a PLACE of (symbol-function 'FUNC)
for some FUNC. This is analogous to the deprecated flet
.
Any supported PLACE can be specified, though -- cl-letf
isn't only for function bindings.
when do you use them?
When you want to temporarily define (or override) a function. The scoping rules you require for any given use-case will determine which option you would use.
(cl-flet ((FUNC ARGLIST BODY...) ...) FORM...)
FUNC is visible only to the code in FORM.
(cl-labels ((FUNC ARGLIST BODY...) ...) FORM...)
FUNC is visible both to the code in FORM and also to the code in FUNC's own BODY.
(cl-letf (((symbol-function 'FUNC) VALUE) ...) BODY...)
FUNC is visible to absolutely everything until BODY has finished being evaluated.
Some (fairly contrived) examples...
In the first example, the temporary function we have defined is recursive -- it calls itself -- and therefore we use cl-labels
:
(n.b. this isn't a robust factorial implementation; it's just for demonstration purposes.)
(defun my-factorial (number)
"Show the factorial of the argument."
(interactive "nFactorial of: ")
(cl-labels ((factorial (n) (if (eq n 1)
1
(* n (factorial (1- n))))))
(message "Factorial of %d is %d" number (factorial number))))
If you change cl-labels
to cl-flet
you will get an error as soon as the inner (factorial (1- n))
is evaluated, because within our temporary function, no function factorial
is known.
If you were to then define a global factorial
function which unconditionally returns the value 1
:
(defun factorial (n) 1)
Then the factorial
function defined by cl-flet
will see that when it calls factorial
, and my-factorial
would calculate (* n 1)
as the value for any argument n
.
When no recursion is needed, cl-flet
is fine to use:
(defun my-square (number)
"Show the square of the argument."
(interactive "nSquare of: ")
(cl-flet ((square (n) (* n n)))
(message "Square of %d is %d" number (square number))))
Both cl-labels
and cl-flet
provide lexically-scoped functions, visible only to the code written within the bodies of those macro calls; and in particular not to the code of any other functions which we may be calling.
If you are defining a helper function such as in the examples above, lexical scoping is probably what you want, as there's a reasonable chance you'll only be calling your helper within the macro body.
If you are trying to temporarily override an existing function, however, there's a pretty fair chance you'll need the functions you're calling to see the override. In such cases you need the override to have dynamic scope.
In the past flet
was the way to provide dynamic scope for temporary functions, but flet
is now deprecated in favour of using cl-letf
with a 'place' of (symbol-function 'FUNC)
In the following examples, the multiplication function is overridden, and the dynamic scope means that my-square
and my-factorial
will see and use our temporary definition of multiplication.
(defun my-bad-square ()
"Maths gone wrong."
(interactive)
(cl-letf (((symbol-function '*) '+))
(call-interactively 'my-square)))
(defun my-bad-factorial ()
"More maths gone wrong."
(interactive)
(cl-letf (((symbol-function '*)
(lambda (x y) (- x y))))
(call-interactively 'my-factorial)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With