Image-Based development and Interactive Experience

Hello everyone,

I am new to Racket and was wondering if and how it is possible to have a decent experience (emacs/geiser) in terms of interactive development similar to common lisp. Is there something that allows to recompile just specific parts of the code without re-compiling everything? Like just a function/class definition etc...? Is it possible to have resumable exceptions?

1 Like

I think this conflicts with the philosophy of the Racket module system. I know what you are talking about, you can do this in elisp, for example, and in common lisp, but things can get out of sync, so it is a bit of a hack. Is compiling whole modules too slow?

One thing I can suggest is to put the stuff you would normally do in the repl into a test or main submodule. This will recreate your state. Resumable exceptions would be nice, but again, that Lisp Machine type of hacking conflicts with the whole idea of the Racket module system--it will actually recompile modules multiple times for different contexts just to avoid leaking the kind of state you desire (see the paper on the module system for details).

1 Like

Obligatory links:

Disclaimer: I haven't tried the library myself.

What do you mean (or what is) "the whole idea of the Racket module system" ?

Geiser supports Racket via the geiser-racket Emacs package. Have you tried it?

1 Like

A Racket module can be instantiated more than one time at more than one phase, so what exactly would it mean to change a definition in the module from your editor without reloading? That is what I was getting at.

Back in the day, manually selecting a package to rebuild in CL, or sending a definition from the editor landed you in a potentially inconsistent state. You had to think through what dependent packages you might have to manually reload, based on what macros and eval-when clauses (primitive version of Racket phases) and state was stale. For general development, it done mainly because memory was measured in megabytes, and CPUs in megahertz. The blog post above indicates that Racket made a deliberate choice to avoid this situation.

There are still good reasons to use hot reloading when you need to preserve the environment (to avoid restarting emacs, to avoid losing video game test state, to avoid rebooting the operating system, etc.). In Racket, I think you have to design your program around the issue rather than leaning on a debugger. In addition to the links above, there is discussion in:

Alternative (not replacement) REPL with Common Lisp style REPL of incremental development · Issue #4645 · racket/racket · GitHub

3 Likes

What is a scenario for this? Why would I want to instantiate a module more than once? and in multiple phases?
Sorry I am trying to understand.

Ok but somebody linked some articles and racket-reloadable, so are those working hacks to make racket more image-based?

In any modular system, if the modules are parameterized, then you will probably want to instantiate them more than once. In the case of Racket, modules are parameterized by phase level. The syntax at phase n is implemented at phase n+1. The syntax of phase n+1 is implemented at phase n+2. Hopefully that is accurate.

Apparently. I haven't looked into dynamic module reloading in Racket. I have used Racket to dynamically load C libraries. You never get a sane hot loading runtime system for free. Somebody has to specify the protocols and implement it. That could be the Posix environment and your C debugger, the CL metaobject protocol, the emacs/lisp system of symbol properties and hooks, or something you cooked up to remote debug a game console, hitting reload in your user agent/browser. People have implemented all kinds of dynamic systems.

Racket is all about the flexibility, so people can build all kinds of things with it. At the same time, Racket is all about the modularity; it should be possible to write code in a way and not worry about what a particular language form might mean later after someone recompiles it. To that end, I would suggest that Racket is kind of "all in" on the idea of separate compilation; a Racket "module" is, by default, sealed, except when its author explicitly provides a means to extend it.

This is definitely different from classic Lisp, classic Smalltalk, and the "Workbook" philosophy of Jupyter and similar systems. I would argue that the basic question is whether it's more important to you to be able to develop reliable software that is free from defects and will work reliably in the future, or whether it's more important to be able to get results quickly, and avoid recomputing potentially very expensive pieces of data. Both are totally legitimate work modes!

Wow, that's a lot of me mouthing off... I must be trying to avoid real work. Anyhow, apologies for anything I've gotten wrong, or any toes I've stepped on.

2 Likes

Which paper?
I can't find a link in the Racket Reference or Guide.

:beetle:

Matthew Flatt, "Composable and Compilable Macros, You Want it When?"

But, read all the PLT papers, and the old Scheme and Lisp ones! Also the paper @samth recently mentioned:

"Advanced Macrology and the Implementation of Typed Scheme", by Ryan Culpepper, Sam Tobin-Hochstadt, and Matthew Flatt has section "3.2 Modules, or You Want it When, Again?"

1 Like

It would be great to have a full featured debugger and an image based environment, etc., but it would be a lot of work, just like anything else :).

I think part of the issue with hot patching would be just figuring out the desired semantics. If you actually did a bunch of work to track versions of code and user interaction and data to rebuild the environment correctly, in the end it might look a lot like you just kept your data in source files and hit F5! That is why I mentioned that in CL it was mostly a time-saver to reload just one package.

In reality, It seems like version control, unit testing, and databases have turned out to be more important than debuggers and images.

If I could magically improve the debugging experience for Racket, I think it would be some kind of debug mode for dynamic failures like contract violations and rackunit failures, like maybe automatically rerunning the code with some kind of tracing enabled. Maybe @ungsams has something really cool in mind?