19

I have some code that uses flet to temporarily change the behaviour of functions.

;; prevent changing the window
(flet ((pop-to-buffer (buffer &rest args)
                      (switch-to-buffer buffer)))
   (compilation-next-error-function n reset))

However, Emacs' byte compiler gives a warning:

In ag/next-error-function:
ag.el:103:7:Warning: `flet' is an obsolete macro (as of 24.3); use either
    `cl-flet' or `cl-letf'.

However, cl-flet works differently and doesn't suit some use cases, and I believe this is one of those cases.

The options I can see are:

  1. Tell Emacs to ignore the warning somehow.

  2. Roll my own flet.

  3. Use noflet or dflet.

What is the best approach?

Wilfred Hughes
  • 6,920
  • 2
  • 31
  • 60
  • 4
    "However, cl-flet works differently". There's a second suggestion in that warning message. ;-) – Malabarba Nov 12 '14 at 19:31
  • I took the old one and just renamed it (e.g., my-flet) and removed the obsolete warning, and use that in in any package that previously required flet. – lawlist Nov 12 '14 at 19:54

2 Answers2

14

To get the same behavior you're used to with flet, use cl-letf to change a symbols function value.

(cl-letf (((symbol-function 'pop-to-buffer)
           (lambda (buffer &rest _) (switch-to-buffer buffer))))
  (compilation-next-error-function n reset))

If you were to roll your own flet, the easy way would be macro that expands to a cl-letf form.

Edit: rolled it

(defmacro myflet (bindings &rest body)
  "Works like the old `flet'. Does not validate form structure."
  (declare (indent defun))
  `(cl-letf ,(mapcar (lambda (binding)
                       `((symbol-function ',(car binding))
                         (lambda ,(cadr binding) ,@(cddr binding))))
                     bindings)
     ,@body))

(myflet ((+ (&rest args) (apply '- args)))
  (+ 10 3 2)) ;; => 5
Jordon Biondo
  • 12,455
  • 2
  • 43
  • 62
  • 4
    However, keep in mind the caveat from Yann Hodique's comment to Malabarba's article: "note that Emacs 24.3.1 has a subtle bug that makes cl-letf not a proper alternative (IIRC, it does not work for flet-ing symbols that are not already fboundp). Which is why a backward-compatible version of flet is a bit... convoluted." – phils Nov 13 '14 at 21:32
14

Artur Malabarba wrote this up recently, in Understanding letf and how it replaces flet.

letf is an alias for cl-letf, and while it's most likely what you want, there's a minor caveat which Artur points out:

Unfortunately, cl-flet is not identical to the original flet—it's lexical, not dynamic.

(Nic's noflet package provides extended functionality along these lines.)

Malabarba
  • 23,148
  • 6
  • 79
  • 164
sanityinc
  • 2,921
  • 14
  • 17