Devlog: a sketch of this application

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:

A sketch of the application, showing a menu (main app actions), toolbar with selection & creation tools, a module canvas, an inspector, and a status bar
The initial view, very "Windows 95" UI.

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.

A sketch of a cell, showing one side for UI display, another side for the value source code, and a number of properties (position, dimensions, value source code, display source code)
A "cell" is a dynamic variable

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!

A sketch of a function, showing its name, parameters, and a canvas for the body (holding its own set of variables and a return cell)
A "function" is a scoped box with some input parameters

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.