Matteo
Matteo

Reputation: 529

Change program code while running in Chicken Scheme

Is it possible to update the program code while it is being interpreted by csi, the Chicken Scheme Interpreter? If so, how?

So that I can interactively change part of the code and immediately see the effects of that changes. For example, suppose I have written the following program:

(define (loop)
   (print "Ciao")
   (rest 1)
   (loop))

(loop)

(assume (rest 1) has the effect of pausing the program for a second).

If I run this program, trough csi, it prints the string "Ciao" every second. If I change the string "Ciao" into something else, for example into "else", and I save the program code file, then csi continue interpreting the old program code, so I continuously see the string "Ciao". I'd like, in this case, that when I save the modified code with the string "Ciao" replaced by "else", csi continue its interpretation job by looking into the modified file, instead of the old file. So that I obtain as output some "Ciao" followed by some "else": the "else" start to appear when I replace "Ciao" by "else" in the source code.

Upvotes: 1

Views: 612

Answers (2)

Sylwester
Sylwester

Reputation: 48765

There is no common way to make a running program check its source for changes but you there seems to be enough feratures available in Chicken to roll your own:

(use posix)
(use srfi-18)

(define (watch-reload! file)
  (define (tsleep n)
    (thread-sleep! (seconds->time (+ n (time->seconds (current-time))))))
  (define (get-time)
    (file-modification-time file))
  (thread-start! 
   (lambda ()
     (let loop ((filetime '())) 
       (let ((newtime  (get-time)))
         (when (not (equal? filetime newtime))
           (load file))
         (tsleep 10)
         (loop newtime))))))

Now all you have to do is to use watch-reload! instead of load and it will check and reload every 10 seconds if the file has been modified. If you save when the file is not valid scheme it stops working until you call watch-reload! on it again.

It may be that chicken programmers might have a better solution.

Upvotes: 2

Inaimathi
Inaimathi

Reputation: 14065

In general, the answer is "don't". The way you're supposed to use the REPL is by evaluating piecemeal changes against it, then evaluating a function or two to make sure everything went as expected. Part of this approach is structuring your program so that it can be easily tested in pieces, which implies not automatically starting any infinite loops. In the specific case you're asking about, you could instead write

(define (do-stuff)
   (print "Ciao"))

(define (main-loop)
   (do-stuff)
   (rest 1)
   (main-loop))

(define (start) (main-loop))

You can now incrementally develop do-stuff, periodically evaluating new versions to your interpreter and calling them to make sure they work, then eventually calling start once you're confident that it's doing the right thing.

You may get mileage out of this similar question asked about Common Lisp.

As an aside, if you were using Common Lisp and SLIME, you could now do more or less what you proposed:

(defun do-stuff ()
  (format t "Ciao~%"))

(defun main-loop ()
  (loop (progn (do-stuff)
               (sleep 1))))

(main-loop)

Starting that up would start printing Ciao on separate lines in your SLIME REPL. If you changed do-stuff to

(defun do-stuff ()
  (format t "else~%"))

then hit it with C-c C-c (the default binding for slime-compile-defun), you'd see your SLIME REPL start printing else lines in-flight.

CL-USER> (main-loop)
Ciao
Ciao
Ciao
; compiling (DEFUN DO-STUFF ...)else
else
else
else
else
else
; Evaluation aborted on NIL. User break.
CL-USER> 

I'm not sure how to accomplish the same thing in Scheme, but I'm reasonably sure it's possible.

All that being said, you sometimes want to run a program part of which is an infinite loop. A real world example would be while testing a TCP server of some sort. If you're in such a situation, and your desired workflow is

  1. Write file
  2. Run csi my-file.scm
  3. Edit file
  4. Kill csi
  5. Run csi my-file.scm
  6. goto 3

and you basically just want to automate steps 4 through 6, you'll need an external tool to do it for you. Take a look at entr or hsandbox (the latter doesn't have Scheme support out of the box, but it doesn't look like it would be too hard to add).

Upvotes: 2

Related Questions