Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I change the code in a lambda while keeping the captured vars?

Lets say I have a REPL to a process running my Common Lisp code. It could be running SWANK/SLIME.

I want to update a function defined with defun in my live process. The function may have captured some variables in a let binding. Essentially, this function is a closure.

How can I update the code in that closure without losing the data that it has captured?

2019-11-03: I selected one answer below, but I recommend reading all of the answers. Each has an interesting insight.

like image 611
drudru Avatar asked Oct 20 '25 03:10

drudru


2 Answers

Basically you can't and that's one of the reasons to avoid using DEFUNs in LETs.

One could create a new closure and try to copy the state from the old one into the new one.

One problem is that portable Common Lisp does not allow much dynamic access to closures. One can't 'go' into a closure and add something or replace something from the outside. There are no reflective or introspective operations defined for closures.

Thus everything you later want to do with a closure needs to be already present in the closure generating code.

Let's say you have this code:

(let ((foo 1))
  (defun add (n)
    n))

Now we decide that add is wrong and should actually add something?

We want the effect of this:

(let ((foo 1))
   (defun add (n)
     (+ n foo)))

How can we modify the original? We basically can't.

If we had:

(let ((foo 1))
  (defun get-foo ()
    foo)
  (defun add (n)
    n))

We could do:

(let ((ff (symbol-function 'get-foo))
      (fa (symbol-function 'add)))
  (setf (symbol-function 'add)
     (lambda (n)
       (+ (funcall fa n) (funcall ff)))))

This then defines a new function add which has access to the closure values via the old functions - captured in its own closure.

Style

Don't use LET enclosed DEFUNs:

  • they are hard to modify
  • the compiler does not see the DEFUNs as toplevel forms
  • the effects are difficult to control
  • what do we want to do with loading code multiple times?
  • they are difficult to debug
  • Common Lisp has ways to deal with global state.
like image 138
Rainer Joswig Avatar answered Oct 21 '25 22:10

Rainer Joswig


You can't from the outside.

You could try to provide helper functionality for that in the same lexical scope. This might entail creating an ad hoc function registry therein.

Another way would be to use dynamic variables, but of course that just breaks open the closure.

Maybe relevant (from https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html):

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."

Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.

like image 37
Svante Avatar answered Oct 21 '25 21:10

Svante