CARVIEW |
Navigation Menu
-
Notifications
You must be signed in to change notification settings - Fork 23
Description
This is the first grant proposal intended for the grants program made possible by the partnership with Status to support the development of Nim.
The goal of this proposal is the implementation of hot code reloading capabilities for the native targets of Nim (C/C++/Obj-C) and consequently a REPL-like tool and a fully-functional Jupyter kernel based on these capabilities.
I've recruited a suitable candidate for the grant - Viktor Kirilov, known for the creation of doctest, a popular unit-testing library for C++, and RCRL, a REPL-like environment for C++ based on similar mechanics to the ones described here.
Viktor is also a blogger and an experienced conference presenter (he will be giving a talk about RCRL later this year at CPPCON). After completing the technical aspects of the project, he will prepare a demo program, a blog post and a technical talk promoting Nim and the newly developed capabilities. The content will be optimized to target game developers. A different volunteer will be selected to create similar promotional content intended for data scientists.
The timeframe of the grant will be 4 to 5 months of full-time work (Oct 2018 - Feb 2019, also known as Nim Winter of Code ;P). Milestones, described below, will be inspected along the way. I'll be mentoring Viktor and reviewing all of the submitted work. The proposed budget for the grant is 20,000 EUR.
Technical details of the proposal and suggested milestones
1. Support the --hotCodeReloading
option in the native targets of Nim
Currently, hot code reloading is supported only the JavaScript target with semantics described in the Nim compiler user guide. This proposal intends to change the semantics to the following:
The hotCodeReloading
option enables special compilation mode where changes in the code can be applied automatically to a running program. The code reloading happens at the granularity of an individual module. When a module is reloaded, newly added global variables will be initialized, but all the other top-level code appearing in the module won't be re-executed and the state of all existing global variables will be preserved. One can use the special event handlers beforeCodeReload
and afterCodeReload
to reset the state of a particular variable or to force the execution of certain statements:
var
settings = initTable[string, string]()
lastReload: Time
for k, v in loadSettings():
settings[k] = v
initProgram()
afterCodeReload:
lastReload = now()
resetProgramState()
The code reloading event handlers can appear in any module multiple times. By default, on each code reload, Nim will execute all handlers defined in the entire program. If you want to prevent this behavior, you can guard the code though the hasModuleChanged
magic:
import mydb
var myCache = initTable[Key, Value]()
afterCodeReload:
if hasModuleChanged(mydb):
resetCache(myCache)
The hot code reloading is based on dynamic library hot swapping in the native targets and direct manipulation of the global namespace in the JavaScript target. The Nim compiler does not specify the mechanism for detecting the conditions when the code must be reloaded. Instead, the program code is expected to call performCodeReload()
every time it wishes to reload its code. The hotcodereloading
module provides easy-to-use helpers for implementing hot code reloading in GUI applications, servers based on async event loops and other types of programs. The nim-livereload
NPM package provides a convenient solution for JavaScript projects based on LiveReload or BrowserSync.
Rationale for the new semantics and description of the intended usage:
The new semantics were chosen, because they are deemed more flexible than the previous ones:
-
Just like before, you have a precise control of which variables and statements
will be executed on each reload. -
You can now execute code in module
A
even when the change happens in moduleB
. You get precise control when this should happen.
How does this work in practice? You would keep your code open in your favourite text editor. If you are making a change affecting only the behavior of certain functions, you just save your code and as soon as it's recompiled you'll see the new behavior in the running program (e.g. you can modify a view function in Karax or a rendering algorithm in a game and immediately see the results on the screen. See the famous talk Inventing on principle by Bret Victor as an inspiration). If you want to send a command to a running program, just like in a REPL environment, you can modify some of the afterCodeReload
blocks or add a new one. The specified code will be executed immediately with the next reload. You can use this to make arbitrary manipulations to the state of your program and you can also use it to inspect the state of any variable by sending it to a logger or a visualization routine. Environments similar to SLIME may hide this interaction behind a simpler interface that sends lines of code for execution or evaluates selected expressions interactively.
Implementation details:
The codegen will ensure that all calls and global var references are routed through patchable jump tables. The program will be compiled to a dynamic link library (DLL, SO, etc), which will be hot swapped on each reload. It will inspect and patch the jump tables immediately after being loaded. The required indirections are expected to lead to a small, but tolerable performance hit which will affect only the development builds of the program. The initial implementation will target portable C, but some future directions will be provided for exploiting platform-specific mechanisms such as the hot patching support offered by some compilers. The initial implementation will signal any modification of a data type between two reloads as an error, but future versions may be able to support modifications to traced garbage collected objects by allocating new copies of these objects and assigning the newly added fields to a default value. Please note that the user may also use the beforeCodeReload
and afterCodeReload
event handlers to serialize the state of an arbitrary program to memory and then re-create it immediately after the reload.
2. Implement a REPL-like console for Nim
It may not be obvious, but the capabilities described in the previous section can be used to implement a REPL for Nim.
-
When the REPL is started, it creates an empty program file behind the scenes.
-
Each time you enter a line in the REPL, this line gets inserted either as a new global variable or inside an
afterCodeReload
handler. Entered expressions are automatically wrapped in anecho repr(e)
call. -
The REPL is responsible for recompiling the program behind the scenes and for cleaning up all compilation artifacts after the session is over.
3. Implement a Jupyter kernel for Nim
-
Similar to a REPL, a Jupyter kernel will just have to maintain a behind-the-scenes program file where each cell is being compiled to a function.
-
Executing cells on demand is equivalent to briefly adding a
afterCodeReload
placing a call. -
A library with overloads for different types is responsible for turning the Nim expressions at the end of each cell into visualizations that can be displayed by Jupyter.
4. Develop an interactive demo
This may be a project based on OpenGL, where the user can interact and modify a simple 3D scene by live editing the code. Suggestions and ideas are welcome.
5. Blog post
Viktor will publish a blog post describing the features and the developed demo and try to spread the word in all the relevant communities.
6. Tech talk
A tech talk will be prepared and proposed to a conference focusing on game development (Viktor has a background in game development and we believe this is the audience that will benefit the most from the new features).