Anular una función localmente, pero permitir llamadas a la función original

7

La función de asesoramiento permite modificar el comportamiento de una función a nivel mundial. Una definición de consejo puede hacer llamadas a la función original.

(defadvice foo
  (around foo-bar activate compile)
  "Always set `qux' to t when running `foo'."
  (let ((qux t))
    ad-do-it))

El clpaquete proporciona la fletmacro para anular una función localmente.

(defun foo ()
  "global")
(flet ((foo ()
          "local"))
  (some-code-that-calls-foo))

Eso no permite una referencia a la foofunción original . ¿Qué pasa si la anulación local necesita llamar a la función original?

(defun foo ()
  "global")
(flet ((foo ()
          (concat (foo) "+local")))
  ;; this will cause an infinite loop when (foo) is called
  (some-code-that-calls-foo))

Este enfoque directo no funciona, por una buena razón: (foo)es una llamada recursiva a la definición local.

¿Cuál es una forma no engorrosa de anular localmente una función, que permite llamar a la función original desde el código de anulación?

Aplicación: parchear algunos códigos existentes, en un caso en el fooque no se debe rebotar globalmente, pero el código debe llamar al original. Aquí está el último ejemplo que he estado esperando:

(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (flet ((buffer-file-name (&optional buffer)
           (<global buffer-file-name> (buffer-base-buffer buffer))))
      ad-do-it)))

Quería volver a unir buffer-file-namelocalmente y llamar al original buffer-file-name. Ok, en este caso específico, hay una solución alternativa, que es usar la buffer-file-namevariable. Pero el punto de mi pregunta aquí es la técnica general. ¿Cómo puedo vincular una función (aquí buffer-file-name) localmente pero llamar a la definición global desde mi redefinición?

Esto es para mi .emacs, que sigo trabajando en Emacs 19.34, por lo que las soluciones que requieren Emacs 24.4 están fuera. Sin embargo, prefiero soluciones que hagan frente a la unión léxica limpiamente, pero el parcheado de mono es inherentemente una unión dinámica.

Gilles 'SO- deja de ser malvado'
fuente
No estoy seguro de que cl-letfesté disponible en emacs 24.3 y anteriores, pero aquí hay un Q&A relacionado: emacs.stackexchange.com/a/16495/221
François Févotte

Respuestas:

6

Almacene la función original (obtenida con symbol-function) en una variable local y úsela funcallpara llamar al objeto de función almacenado en esa variable. Incómodo, pero en su mayoría funciona.

(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (let ((original-buffer-file-name (symbol-function 'buffer-file-name)))
    (flet ((buffer-file-name (&optional buffer)
             (funcall original-buffer-file-name (buffer-base-buffer buffer))))
        ad-do-it)))

Esto funciona principalmente , ya que hace lo que se supone que debe hacer, pero puede romperse en circunstancias excepcionales. Dado que Emacs Lisp no tiene una forma primitiva de definir localmente las ranuras de función de los símbolos (solo ranuras variables, con let), fletestablece los enlaces y los restaura unwind-protect. Si el código muere porque se excedió el anidamiento de la llamada max-lisp-eval-depth, o si el enlace se modifica durante la ejecución de esta función (por ejemplo, porque está depurando el consejo), es posible que la ranura de función del símbolo no se restablezca. Es posible que desee tomar precauciones contra la pérdida accidental de algunas funciones.

Otro método es almacenar una copia de la función. Esto tiene la ventaja de que la función original nunca se pierde.

(fset 'original-buffer-file-name (symbol-function 'buffer-file-name))
(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (flet ((buffer-file-name (&optional buffer)
           (original-buffer-file-name (buffer-base-buffer buffer))))
      ad-do-it)))

Esto estaría bien en este caso específico, porque buffer-file-namees una función incorporada que es poco probable que se recupere, pero no rastrearía las redefiniciones de la función global (por ejemplo, para agregar consejos a esa función).

Gilles 'SO- deja de ser malvado'
fuente
¿Tienes una nadvicereceta para lo mismo?
Kaushal Modi
1
@kaushalmodi No. Solo utilizo funciones nuevas si tienen un beneficio definitivo. Todavía uso 24.3 a diario, así que sigo el mecanismo de consejos que funciona desde Emacs 19 (¿o antes?).
Gilles 'SO- deja de ser malvado'