As summarized in the reactor issue "WIP: convert JS to Elm", moving as much of the frontend of elm-reactor from JS to Elm as possible would have significant code quality benefits.
As listed in the issue:
- Use sliders from elm-html, not a custom native library
- Move logic for toggling the sidebar and event blocker into Elm
- Display errors with Elm, not with crazy JS
- Possibly create trace canvas in Elm and render it with Graphics.Collage
Sketch of types needed to represent the state (see debugger-implementation.js for current JS datastructures):
type alias DebugState =
{ runningState : RunningState
-- , totalTimeLost : ?
, events : Array Event
, snapshots = Array Snapshot
-- async callbacks?
-- traces : ?
, permitSwaps : Bool
}
type RunningState
= Running
| Paused Time FrameIndex -- ?
type alias FrameIndex = Int
-- EVENTS
-- TODO: add trace updates
type alias Event =
{ id : SGNodeId
, value : ElmValue
, time : Time
, watchUpdates : Dict WatchId ElmValue
}
-- SNAPSHOTS
type alias Snapshot =
{ signalGraph : SGSnapshot
, watches : WatchSnapshot
}
-- signal graph
type alias SGSnapshot = Dict SGNodeId SignalValue
type alias SGNodeId = Int
type alias SignalValue =
{ id : Int
, value : ElmValue
}
-- WATCHES
type alias WatchSnapshot = Dict WatchId ElmValue
type alias WatchId = String
Sketch of tasks and ports needed to interact with the runtime system:
-- incoming
port events : Signal Event
-- outgoing
port captureSnapshot : Signal (Task e Snapshot)
port captureSnapshot = ...
port setToSnapshot : Signal Snapshot
port setToSnapshot = ...
port processEvents : Signal (Array Event)
port processEvents = ...
While the runtime system currently stores all of the state above, in the proposed refactor it would only:
- Push events to the Elm code as they happen (into the
events
port) - Capture snapshots of the signal graph (handling the
captureSnapshot
port) - Set the signal graph to a given snapshot (handling the
setAtSnapshot
port) - Push a given event into the signal graph (handling the
processEvents
port)
The sketch above only describes what happens while debugging the current version of the code. What happens when we want to swap? Currently the new code gets sent over a websocket, triggering the RTS to swap it in. If any of this was moved to Elm, the Elm code would:
- Initialize the websocket connection
- On new code being sent, send the new code to an output port.
- Replay user input from the beginning
This would require a new port: port newCode : Signal String
- Converting snapshots between JS and Elm: Snapshots are captured by the RTS as JS data structures and need to be in JS form when we set the signal graph state to that snapshot. If they are stored in Elm, we need to convert to and from Elm on each of these operations.
- Passing slices of the event list to JS (for
processEvents
), and appending lots of events to the event list in theDebugState
value. - Drawing traces