Devlog: a sketch of this application
Last updated
Time for devlog #3! Last week's was here.
This time I'd like to describe the "minimum viable" application that I'm building.
A picture is worth a thousand words, so here are a few showing the overall ideas.
The application
Software is comprised of modules, which are the main unit of organizing a program.
I've sometimes wanted "comments" in my code which were live representations of what the program does.
Like, a "comment" could be an instance of a visual UI component being built with some edge-case data, or it could be a red/green light showing the pass/fail state of a unit test, which verifies outputs match certain inputs.
With that in mind, imagine an application which is meant to help you understand, build, and maintain modules:
Instead of a module being a bunch of lines of source code, what if it was an arrangement of objects? Each could represent a declaration/statement present in the module.
Selection tools could be used to arrange these objects in a way which best reflects the intent of the source code.
Creation tools could be used to create new objects in the module itself, and the Inspector could be used to modify the properties of these objects.
But what are the objects that could live within the module canvas?
The "Cell"
In order for variables to be presented as UI, we could think of them as having two distinct, but related parts.
- Its "value" holds the runtime value associated with the variable
- Its "display" holds arbitrary UI to show (and even interact with) the value the variable holds
This "display" could be like a visual comment: something to help illustrate the cell's meaning.
Inside of the inspector, you may view all of a cell's properties:
- The position & dimensions (so a cell can be positioned within the canvas)
- The source code for the Value (dynamically evaluated to get the Value)
- The source code for the Display (dynamically evaluated to get the Value)
And each of these cell's properties (even position & dimensions) may also be dynamically evaluated code.
Like how here, the follower
cell follows the position of the target
cell: Link to early build showing two cells.
The idea here is that a cell is a name binding within the module.
And when accessed in code, a cell is represented as an object which can be interacted with in order to get the value (and metadata).
I think that it'd make sense for the display
property (and position
& dimensions
) of the cell to only exist at
"dev time" and "debug time", but not be present in production builds.
This "flip to see under the hood" metaphor seems neat, but I don't know if it actually would be useful.
Functions
If cells are like variable declarations, then we need functions too!
In code (at least in JavaScript), a function:
- Has a lexical scope, so its code can read its own names and the names declared "outside" of the function
- Has a number of parameters, which are inputs to the function
- May have a
return
value, which is the output of the function
So maybe a function is just a "box" drawn around a portion of a module, which may itself contain other objects!
In the inspector, the function parameters could be changed and be given default values.
These function parameters then act just like "cells" that could be referenced like any other cell contained within the function itself.
And the return value could also just be a "cell" which evaluates to an arbitrary value.
It's not depicted in the image here, but I'd imagine that a "function" could also have a special "display" return value, separate from the actual "return" cell.
This could allow the function to also have a visual representation.
Perhaps "instances" of a function could be created as objects in the canvas, which take input parameters and are displayed as the UI drawn via this "display" return value. And code reading these "instances" would read the "return" value of the function.
Other objects
Besides "cells" and "functions" (and these "instances"), we would also probably need a few other objects:
- Collections could be dynamic arrays of values, which have a display function to show how a single value would look
- Module imports could be presented as objects imported by name from another module
- I'm thinking I/O and external state (like network, time, disk, etc...) could be done via special built-in modules
- Side effects could map to arbitrary effectful statements -- though I'm not sure how necessary this is...
- Comments could be like pure "display" cells -- they don't hold values, but they hold UI that can be used to describe and interact with the code
There probably are others, but I think with just that, it'd be possible to build fully functioning applications with this sort of tool.
Serialized as ... code?
The underlying representation of a module (its "cells" and objects, their positions and attributes) could just be code itself.
Like, our target/follower example module could be serialized like this:
example.js
import JSX, { Cell } from 'runtime';
export const target = new Cell({
$x() { return 0; },
$xPx() { return `${this.$x.get()}px`; },
$y() { return 0; },
$yPx() { return `${this.$y.get()}px`; },
$value() { return "Drag me around!"; },
$display() { return (
<div
class="CellRenderer"
style:top={this.$yPx}
style:left={this.$yPx}
>
<b>Name: {this.$id}</b>
<div>Name: {this.$value}</div>
</div>
); },
overrides: {
$x: 1747,
$y: 1817,
},
});
export const follower = new Cell({
$x() { return target.$x.get() + 100; },
$xPx() { return `${this.$x.get()}px`; },
$y() { return target.$y.get() + 100; },
$yPx() { return `${this.$y.get()}px`; },
$value() { return "Drag me around!"; },
$display() { return (
<div
class="CellRenderer"
style:top={this.$yPx}
style:left={this.$yPx}
>
<b>Name: {this.$id}</b>
<div>Name: {this.$value}</div>
</div>
); },
overrides: {
},
});
This would be extremely powerful, since it would allow these modules to be tracked in git
or any other SCM, be linted
/ typechecked with existing tools like eslint
/ typescript
, or even maybe built with standard tools like esbuild
!
Building
Or maybe building is something done by the app itself?
One way to "build" a module into an application/library would be to choose a single cell in a module as the "entry point" and let its value be built in a number of ways:
- "render" the cell's value into HTML, which could give you an interactive web application
- "export" the cell's value as an object which has
{get, set, subscribe}
methods, allowing code to read, write, and subscribe to changes to a cell
Perhaps that's all that's needed initially?
It'd be interesting to see how debugging could also be done in this application. Under the hood all data is tracked in a big dependency graph, so it should be possible to record all of the data in this graph over a period of time, which could lead to a sort of "time travel debugging" experience.
Anyway, this is what I'm building. I'm nearly done getting functions working, which will be my next deploy.