News

Hot Reloading Plans

Hot reloading is one of the two most important features for Shim (the other being time-travel debugging). Hot reloading lets you update the code for a live running application. For example, you could update how a character moves in a game, tweak UI layouts, or quickly iterate on gameplay concepts. It’s also very useful for adding debug drawing to better understand bugs. Hot reloading and time travelling work hand-in-hand. If there’s a bug you can time-travel back to the frame with the issue and add debug drawing to see what’s going on frame-by-frame.

Shim is a bit more capable than some other systems for hot reloading. In particular most hot reloading systems don’t allow you to add/remove members from objects/structs. There are practical limitations to what can be supported, but here’s a list of features that will be explicitly supported:

  • Changing the body of a function
  • Changing the signature of a function
  • Adding functions
  • Removing functions
  • Adding fields to a struct (if there’s a default)
  • Removing fields from a struct
  • Adding methods to a struct
  • Removing methods from a struct
  • Reordering fields in a struct

Other hot reloading features introduce some additional challenges.

Handling Closures

Handling closures adds some complications that may not be able to be resolved. It’s something that would be nice, but the workaround is to create a struct that is named and explicitly holds the values that the closure would otherwise capture.

Handling Globals

Another uncertainty is how to handle globals. It would be very nice to be able to live reload constants. If that’s not possible, then I feel like it would push devs to put all globals behind function calls. Another option is to add a GDScript-like annotation like @reload to constants that should have their value overridden.

@reload let BUTTON_SPACING = 40
Press the play button to execute

There’s a reasonable argument that state manipulation belongs in the domain of the debugger. While that is relevant to the debugger, that’s not a reason to exclude that functionality from live reloading. Whatever value is determined by tweaking things in the debugger is going to be copied over to the source code anyways.

Handling Field Renaming

Field renaming isn’t explicitly supported, but it can be done with clever use of defaults.

Initial state:

struct Player {
    speed,
}
let p = Player(5.0)
Press the play button to execute

Step one, create a new velocity field with a default value of the speed field:

struct Player {
    speed,
    velocity=speed,
}
let p = Player(5.0)
Press the play button to execute

Step two, remove the original field and the default expression for velocity:

struct Player {
    velocity,
}
let p = Player(5.0)
Press the play button to execute

Alternatively, Shim could assume that if a struct has the same number of fields that those fields were just renamed, but that would prevent field reordering. I think field reordering is a nice refactoring, especially for structs with many fields with default arguments.

Handling Struct Renaming

Struct renaming is difficult to do in a single step since there’s no 100% way to know that “Actor” is now “Player” rather than “Actor” being removed and “Player” taking its place.

Instead you would need to do this in steps, just like field renaming:

Initial state:

struct Actor {}
let p = Actor()
Press the play button to execute

Step one, create a Player variable that is assigned the struct type Actor:

struct Actor {}
let Player = Actor
let p = Player()
Press the play button to execute

Step two, turn the Player variable into a struct and remove the old Actor:

struct Player {}
let p = Player()
Press the play button to execute

Renaming Bound Methods

Currently, there’s no way to completely migrate from one method name to another if there are bound methods floating around. The old method will need to stick around as a stub that points to the new method name.

← Back to all news