0

Does elisp have the "runtime code swapping" feature?

mentioned in slide at 27:14 https://www.youtube.com/watch?v=3TlEqzptWr0&t=27m14s

Hot code loading is the art of replacing an engine from a running car without having to stop it.

Definition of code hot swapping from the PhD of Don Stewart:

Definition Hot swapping is the replacement of existing code fragments at runtime without loss of the environment (state) associated with that code.

Further below he explains:

The goal of code hot swapping is to allow for upgrades to components without observable loss in availability.

7
  • 1
    Please explain what "runtime code swapping" is.
    – Dan
    Commented Oct 29, 2019 at 12:49
  • aka Hot code loading - is the art of replacing an engine from a running car without having to stop it. Commented Oct 29, 2019 at 13:24
  • The question is unclear. No one should need to follow a link to read something or watch something, to try to figure out what you might be asking. Please make the question self-explanatory.
    – Drew
    Commented Oct 29, 2019 at 13:26
  • The link is useless for me. I get a "Video unavailable" error. (Maybe this is country code dependent. Don't know.) The PhD of Don Stewart descibes "hot swapping of an entire application's code". Does that fit your problem?
    – Tobias
    Commented Oct 29, 2019 at 14:02
  • 2
    The answer is "yes, obviously" which is why the question is hard to understand.
    – Stefan
    Commented Oct 29, 2019 at 15:34

2 Answers 2

3

Yes, at any point you can redefine a function by evaluating a defun with the name of an existing function.

By extension, when you reload a file with M-x load-file, all function definitions in that file will be reevaluated, and the corresponding old function definitions replaced.

If you want to change the behaviour of an existing function, it might be cleaner to "advise" that function using advice-add instead of redefining it. See this answer for a description of how to do this.

4
  • nadvice is preferable. You should add a note about that.
    – Tobias
    Commented Oct 29, 2019 at 14:00
  • @Tobias Good point, added.
    – legoscia
    Commented Oct 29, 2019 at 15:03
  • Beware of inlined functions (section 13.13 of the elisp manual): "if you change the definition of the function, calls already inlined still use the old definition until you recompile them."
    – JeanPierre
    Commented Oct 29, 2019 at 18:33
  • 1
    Nowadays one needs to regard the lexical environment of functions defined by defun. Therefore the answer is not so simple anymore. See my answer. I have not seen it so far but in principle one could introduce a namespace for a package through encapsulating the defuns into a let-binding. But, in that case the functions defined in the package become stateful and therefore impure. (See my answer.) I know that this disregards current guidelines for Emacs lisp packages.
    – Tobias
    Commented Oct 30, 2019 at 7:11
3

There is no real definition in Dave Thomas' video presentation. Therefore I stick to the definition Don Stewart gives in his PhD cited in your question.

Dave Thomas mentiones in his video that a functional language "potentially allows runtime code swapping" because "it is pure".

The notion of a pure function means that the function does not have an internal state. The arguments of the function completely determine its output. That would fit nicely to Don's definition.

It also gives a hint how your question must be answered: The answer is: "Yes and No depending on the code."

Yes

Function definitions

Nowadays one is encouraged to use lexical binding.

Let us define a function f on lexical top-level. In the following Elisp code fragments the evaluation results are given in the comments following the Elisp forms.

(defun f (x)
  "Pure function"
  (1+ x))
;; f

(symbol-function 'f)
;; (closure (t) (x) "Pure function" (1+ x))

The lexical environment does just hold a t which is the symbol standing for "true" and evaluating to itself. The closure holds no state at all and we have a pure function which can be replaced by binding f's function cell to another (pure) closure.

If we consider the contents of the value cells of all symbols as the current state of the Elisp runtime then swapping the pure function does not change the state of the Elisp runtime.

Note: Here we completely ignore that the function cells of the symbols actually also contribute to the state of the runtime. But that is also covered by Don's definition because the values of the function cells more or less represent the code fragments whose Don is speaking of.

Library reloading

Symbols for variables of a library should be declared as special by defvar on top-level of the library. A default value can be passed as argument to defvar. The value cell of the dynamic variable is is only bound to the given default value if it is not yet bound, i.e., it is not set yet.

If only devar and defun are used on top-level of the library and no setq or function evaluation then the state of the library is even preserved when one reloads the library with (load-file "someLibName.el") or (load-library "someLibName.el").

Thereby, the newly loaded library can also be a modified version of the first-loaded one. Naturally, it should use its dynamic variables in a compatible way.

No

Function definitions

Closures get a lexical environment with lexically bound symbols if they are defined in the context of a (nontrivial) let-binding.

If one redefines the closure in a separate evaluation of the (same) let form the newly defined closure has a fresh lexical environment. It can no longer access the lexical bindings from the first evaluation of the let binding.

That means that closures with nontrivial lexical environment have an internal state and that state is lost if one redefines the closure through reevaluation of the let form.

The following code demonstrates the effect:

;; -*- lexical-binding: t -*-

(defun my-definitions ()
  (let ((state 1))

    (defun my-set-state (x)
      (setq state x))

    (defun my-get-state ()
      state)))
;; my-definitions

(my-definitions)
;; my-get-state
(defalias 'my-save-fun (symbol-function 'my-set-state))
;; my-save-fun

(my-definitions)
;; my-get-state

(symbol-function 'my-save-fun)
;; (closure ((state . 1) t) (x) (setq state x))

(symbol-function 'my-set-state)
;; (closure ((state . 1) t) (x) (setq state x))
(symbol-function 'my-get-state)
;; (closure ((state . 1) t) nil state)
(eq (cadr (symbol-function 'my-set-state)) (cadr (symbol-function 'my-get-state)))
;; t ;; `my-set-state' and `my-get-state' have the same lexical environment
(eq (cadr (symbol-function 'my-set-state)) (cadr (symbol-function 'my-save-fun)))
;; nil ;; `my-save-fun' and `my-get-state' look very alike but they have differing lexical environments
(my-save-fun 2)
;; 2 ;; setting state of `my-save-fun' to 2
(my-get-state)
;; 1 ;; state of `my-get-state' remains 1
(my-set-state 3)
;; 3 ;; set state of `my-set-state' and `my-get-state' to 3
(my-get-state)
;; 3 ;; Voila...

Library reloading

If one sets the value of dynamically bound variables in a library with setq its previously set value is lost. Thus reloading such a library destroys the state of the runtime.

Needless to say that function evaluation on top-level of a library can also modify the values of the dynamic variables of the library.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.