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,890
  • 2
  • 29
  • 59
  • 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,332
  • 2
  • 41
  • 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](https://github.com/sigma/el-x/blob/master/lisp/dflet.el)." – 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
  • 22,878
  • 6
  • 78
  • 163
sanityinc
  • 2,871
  • 13
  • 17