Date: Thu, 8 Jan 2015 17:06:15 -0800
From: Toby Schachman
Subject: Re: [Journal] New Apparatus abstraction model
Still quite in flux, like a Go board that keeps "flipping", but here is a shot of Stage 2. I've found designing these little notations to be useful.

Inline image 1

On Tue, Jan 6, 2015 at 1:45 PM, Toby Schachman wrote:
(Journal entry for my own reference, feel free to ignore. Though if you do read it, I'd appreciate comments, especially on the terminology / representations I'm using to think about this.)

Continuing the scoping discussion from the column view thread:

The new abstraction mechanism suggested by the column view is to not declare certain variables as the parameters of a reusable diagram (as you would when writing a function in code) but instead just bring in instances of the diagram and modify them arbitrarily within the context of the parent.

For example, you could make a reusable diagram of Two Circles and then in another diagram (called Scene) you make multiple instances of Two Circles. Then to customize a particular instance, you'd just go in an change it--for example you could change the color of one of the circles in one of the instances of Two Circles. Or you could add another child (say a rectangle) to an instance of Two Circles. Changes you make when you're editing Scene will not affect the Two Circles diagram--the changes only affect the specific instances of Two Circles within Scene.

Changes to a definition do, however, propagate to all its instances. For example if you edited the Two Circles diagram to make one circle bigger, then that would affect all the instances of Two Circles in Scene (and anywhere else there are instances).

There are all kinds of tricky traps in designing these semantics. For example, if you change the color of the circles in Two Circles, does this override the fact that you changed one of the circles in Scene (it shouldn't). If you add a triangle to the definition of Two Circles, do all the instances get this triangle too (they should).

I have a good idea about what should and shouldn't happen when you do various modifications but these need to be encoded into principles / rules which can be implemented. Further, I want this implementation to be as simple and small-powerful as possible since it's the foundation of everything else. And it should be simple if the resulting semantics are simple!

The problem has flavors of prototypal inheritance. I also think it is in some way dual to lambda abstraction. Like lambda abstraction from "the other direction".

I'm pretty sure I've worked out a solution, though I'll have to see if any gotchas arise in the coming week(s) as I implement it.

I'm implementing it in stages and I want to have code, examples, and visualization of the semantics at each stage. I'm trying to document my thought process and mental models as I build up understanding since I think this is what's missing when we look at "open source code".


Stage 1: You can construct an instance of a shape. The instance is allowed to have its own children which get added to the prototype's children. Stage 1 is done, it looks like this:

Inline image 1

The top part are the definitions of the shapes (the model). The bottom part is how these definitions get expanded. The rule is when you're editing a diagram, your changes only modify the model (the top part) of the diagram you're editing.


Stage 2: An instance can also override any of the nodes in its prototype. An override is encoded as a target node (which must live in the "top part" of the prototype) and the replacement node. The replacement must (?) have the target as its prototype. The goal is to be robust to moves. That is, if you do ordinary reorganizations (e.g. putting a circle in a group) then all the references are still what you'd expect (so if you colored the circle red in some instance, the circle would still be red even though now it is in a group).

Stage 2 will require passing an "environment" (aka context) as the interpreter recursively expands the tree. This environment, rather than storing variable name bindings will store all the overrides that are in effect within the current context. When the interpreter encounters a node, it will check if it should instead use a replacement node.


Stage 3: Implement "expressions" with references to other nodes, so you can do Excel-like stuff. I'll be using direct pointers in my references (not parent.parent.etc) which you'll create in the UI by dragging (as in DDV and early Shadershop prototypes) or by autocomplete while typing.

I think that if referenced nodes are just replaced appropriately by the "override environment" then everything will "just work"...


Stage 4: Implement the arrays-to-make-multiples feature. This may just end up being another interpreter on top of the Stage 3 interpreter. This is its own design problem but I'm confident that if I can get Stage 3 working then I'll eventually figure out how to layer this feature on top of that.