Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In emacs, what is the difference between cl-flet and cl-letf?

Tags:

emacs

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?

like image 931
UndeadKernel Avatar asked Sep 17 '16 18:09

UndeadKernel


1 Answers

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)))
like image 71
phils Avatar answered Nov 11 '22 10:11

phils