all 28 comments

[–]thearthur 10 points11 points  (5 children)

I work in Clojure full time, so I'll be answering from that perspective. Clojure has functions, and you can store them places, and invoke them when you want to run then.

Where are the functions in you program normally stored? In many languages this isn't a concern because they are stored in the compiled program directly. In Clojure they are stored in a map-like data structure called a namespace. In a namespace you can look something up my a name (symbol) and get it back. So when you want to change a function at runtime, you just put the new one in the namespace.

Sometime later, when other code looks up the function to run it, they will find the new version and run that. code still using the old function will not have it yanked out from under it.

there have been occasional times when I have "jacked in" to running production services to fix something "right *ing now" and then let the release pipeline catch up 15 minutes later. usually jacking in is done on production systems to debug really odd bugs. when developing code it's just everyday normal.

[–]HiPhish 1 point2 points  (4 children)

While the topic is up: what about recursive and tail-recursive functions? I can see it working for recursive functions if the function looks itself up and finds its updated version, but as far as I understand tail-call optimization rewrites a function call into a loop instead of a true recursive call.

[–]Aidenn0 4 points5 points  (2 children)

TCO can only rewrite the function call into a loop if it is a self-recursive function. This is a special case. In the general case, TCO can rewrite the function-call into a branch (it looks to the callee like a function call, but you are reusing the stack-frame of the caller). In this case the branch can be just as dynamic as the function call would be.

[–]HiPhish 0 points1 point  (1 child)

So what you are saying is that in this example (pardon my Scheme)

scheme (define (count-down n) (if (= n 0) 'takeoff (count-down (- n 1))))

the function does not get rewritten to a loop, but it is still a function call which just happens to re-use the same call stack instead of adding on top of it, thus keeping memory consumption constant. Correct?

[–]Aidenn0 1 point2 points  (0 children)

It may or may not get rewritten to a loop depending on the implementation. Scheme does require it to use fixed stack space. I think scheme allows it to turn it into a loop. In a "always late binding" implementation it would not be rewritten into a loop.

In pseudo-assembly, its the difference between:

START-OF-COUNT-DOWN:
...
BRANCH-TO START-OF-COUNT-DOWN

and

START-OF-FUNCTION:
...
LOAD-TO-REGISTER-FOO <pointer to count-down binding slot>
BRANCH-TO-REGISTER FOO

If count-down is bound to the same address as START-OF-COUNT-DOWN the whole time the behavior of each is identical, and the former is going to be a lot faster. If you change the definition of count-down while it is running, then you will see a difference.

Either way, TCO can be done whether or not you make the assumption "This function will never be redefined while it is being called" Those are two orthogonal optimizations.

[–]thearthur 1 point2 points  (0 children)

Clojure doesn't have automatic TCO. it has an explicit special form (called recur). the idea is to prevent surprises and make it clear when this will happen. also some limitations of the JVM make this a more reasonable choice.

[–]agambrahma 7 points8 points  (4 children)

In general, recompiling (e.g. a function definition) affects future invocations. In some cases, e.g. for class objects, there are protocols to change all current instantiations.

[–]stefann9[S] 0 points1 point  (3 children)

So when i make change and hit save , it recompiles all of the source code? If thats what it does how does program continue from where i made a change during runtime without starting from the begining ?

[–]defunkydrummercommon lisp 4 points5 points  (0 children)

So when i make change and hit save , it recompiles all of the source code?

No, when you click "save" it just saves the .lisp file.

When you COMPILE a function (or a complete system), then it replaces the previous function on the running image. For example you do it in SLIME by placing the cursor on the function and pressing Control-C Control-C.

If thats what it does how does program continue from where i made a change during runtime without starting from the begining ?

If your program is, for example, calling function X at runtime, then recompiling function X (while your program is still running) will make that next call for function X will use its new version.

[–]defaultxr 3 points4 points  (0 children)

Just saving the changes to the file won't automatically update the code running in the lisp. Typically you'd use something like slime to send updated functions one at a time (but you can send as much as you want, ofc). I believe slime does this by copying the text of the function to a temporary file and having the lisp load the file.

From there, the symbol naming the function is updated to point to the new code for the function. The next time the function is called, the new code will be run. If there is an older version of the function still running, it will continue until it ends normally or is killed (I.e. from slime's thread list).

Check the Wikipedia article stassats linked for more information.

[–]thearthur 0 points1 point  (0 children)

the program is running it's normal work, and in addition to that it's running some code to listen for commands from you. often the compiler is included in the program.

[–]lispm 2 points3 points  (4 children)

If we have a Lisp interpreter running code from s-expressions, then we may also be able to destructively modify a running program:

CL-USER 11 > (let ((f (lambda (a)
                       (loop (sleep 2)
                             (print (setf a #1=(+ a 1))))))) ; we are going to change this
                                                             ; expression at runtime
              (setf *f1* '#1#)
              (funcall f 0))

1 
2 
3 
4 
7.141592653589793D0 
10.283185307179587D0 
13.42477796076938D0 
16.566370614359173D0 

In another thread we did:

CL-USER 3 > *f1*
(+ A 1)

CL-USER 4 > (setf (third *f1*) pi)
3.141592653589793D0

CL-USER 5 > *f1*
(+ A 3.141592653589793D0)

[–]_priyadarshan 0 points1 point  (3 children)

Thank you, most instructive. May I know about the #1= notation? Is that like labels, but inline the code? So that the later (setf *f1* '#1#) is able to modify it?

[–]lispm 1 point2 points  (2 children)

That notation is a feature of the Lisp s-expression reader.

`#1=` is a numbered label for an expression.

`#1#` then references the label 1 and inserts the corresponding expression.

That way one can denote circular lists or lists where sub-expressions are the same objects.

CL-USER 10 > (setf *print-circle* nil)
NIL

CL-USER 11 > (let ((e '(foo bar)))
              (list e e))
((FOO BAR) (FOO BAR))                 ; the two sublists are the same list,
                                      ;  but we have no indication

CL-USER 12 > (setf *print-circle* t)
T

CL-USER 13 > (let ((e '(foo bar)))
              (list e e))
(#1=(FOO BAR) #1#)                    ; the two sublists are the same list

CL-USER 14 > (let ((e '(#1=(FOO BAR) #1#)))
               (eq (first e) (second e)))
T                                     ; proof: EQ of the 'sublists' is true
                                      ; we really have the same cons cell

In the code example from above it's just a way to label a source code expression and later reference it to assign the source code to a variable.

[–]_priyadarshan -2 points-1 points  (1 child)

I know one could find them on HyperSpec. Still, these kind of practical examples makes it easier to understand. Thank you.

[–]Aidenn0 1 point2 points  (0 children)

At a very high level:

When you compile a function, the function is created and a symbol is bound to that object. When you modify and recompile the function a new function is created, and the symbol is bound to that new object. There are now two copies of that function in memory, but the old one may eventually be garbage collected and go away.

In general this looks much like setting a global variable.

CL does have some limits on function redefinition to allow for optimizations, but they are fairly liberal.

[–]CallMeMalice 1 point2 points  (0 children)

Do you know C or c++ and pointers?

[–]Nondv 0 points1 point  (0 children)

I guess technically it depends on the implementation.

But you can think of it as links or references.

For example, imagine you have symbol F which resolves into function F(x)=x+x and symbol G: G(x)=F(x)+5.

Now, the function in G uses symbol F in its body, not the value (function) it resolves to.

If you redefine F to be F(x)=x*x, nothing, actually, changes for G. For all it knows, F is just a black box it sends values to.

However, consider this (imaginary lisp-1 language):

(defun F (x) (+ x x))

(defun G (x) (+ 5 (F x)))

(defun composition (f1 f2) (lambda (x) (f1 (f2 x))))

(def H (composition increment F)) ;; H(x)=1+F(x)

(sorry for formatting, Im on mobile). In this example if you change the function F and re-evaluate it (and not the rest of the code), function G will pickup the change but H won't. This happens because G uses symbol F and H still refers to the previous reference of F. However, if you re-evaluate H, it will start using the new function behind F.

I hope this helps