Date: Thu, 4 Nov 2021 17:21:26 -0700
From: Bret Victor
Subject: recent Realtalk updates
Here are some Realtalk updates from the last couple weeks.  Most were in preparation for the workshop with Shawn and Konlin (to be written up in some future email), and many of them have to do with improving visibility.


VISIBILITY

Showing statements and matches

You can wish an object "shows its statements" and "shows its matches" (or "shows its match counts" for a more abbreviated view).  Matches appear on the left in purple, and statements on the right in blue.  Each match appears in its own little rectangle, to emphasize its individuality.



Statements are connected to their matches with a line, if both are showing.




Matches and statements sprout from their line of code, whether the line is printed, on a blank, or in the editor.  Pressing control-I in the editor toggles showing statements and matches for lines that are visible.

I usually kept the old match inspector turned off, I think because it was too big and spaced out.  I tried to make the new match annotations more compact and right-justified, so they can be left on all the time.  (Of course, it goes without saying that all of this evolved out of the old match inspector.)



Showing logs

Another handy annotation is showing logs.  These are all using a new "page annotation" feature, which lays out these annotation stacks and draws the connecting line.  



Statements about objects

    When statement /s/ is about (p):

gives you all the statements "about" an object -- that is, with the object in the second parameter of the statement.  (This used to be called "Statements about above" in the dictionary, and before that, the gossip bubble.)  Metastatements and other administrative statements are excluded.

It currently scans the entire frame each tick, so it slows things down a bit (although the system is still usable).  I plan to extend the reactor in such a way that these kinds of things can update reactively.



Matches and notices in editor

Next to the match count, the editor lists all of the objects making statements that were matched.  Next to the notice count, the editor lists all of the objects that noticed the statement.  (Well, up to four.)  The notice count and statement count are also better-formatted



Suggestions in editor

When the cursor in on an incomplete line in the editor, it shows a list of suggestions.  The set of suggestions is taken from lines with the <-- highlight comment, and "reversed" into a suggestion.  (E.g. a highlighted "When /someone/ wishes /p/ prints layer /layer/:" becomes the suggestion "Wish _ prints layer _.".)  The listed suggestions are based on matching the first interesting word in the incomplete line.

This is currently pretty crude, and somewhat incomplete because not all lines that should be suggestions are currently highlighted.  It only shows the relation, without parameter names or documentation.  It doesn't autocomplete into the editor.  But it's been very useful almost immediately.



Line numbers in editor

Line numbers in the editor used to be drawn on the supporter, where they went underneath "unhack the planet" and were basically unreadable.  Now they are drawn on the page, and use a new color scheme which feels pretty good as you edit and save.



Colors

It used to be that text in the editor and printed text used purple for translated text and white/black for plain text, which effectively meant purple for Realtalk and white/black for Lua.

Now, each macro can specify its own highlight style.  I'm using purple for rules ("incoming"), blue for statements ("outgoing"), and green for memories.  It's so effective that, within hours, the old stuff became unreadable.  This color scheme is carried out to the visualizers, which show match information in purple and statement/notice information in blue.




Line bounds

To draw annotations to lines of text on printed and blank pages, I had to give Realtalk a better sense of the geometric bounds of the text.  This also can be seen in the tighter highlighting of lines, such as for patches or errors, which I think looks really nice.




Explaining the frame

I resurrected my frame explainer, and added it to Visibility Kit.  Wishing that you "explains the frame" turns the entire supporter into an explainer world, with the first statement or rule of each page being explained.  




QUERIES

All matches

Matches are now "map-like", with keys and values:

    With all /matches/ for /p/ is a "page", /p/ has title /title/:
        for i,m in ipairs(matches) do
            log(m.p, m.title)
        end
    End

For compatibility, they are also still array-like, and nothing is currently using the new entries yet.  At some point I will switch the rulepages over, and deprecate "iterate_matches".


Histories:   Delayed clauses [(t) seconds ago]

    When /p/ is a "audio signal",
         /p/ has value /delayed_value/ [(0.5) seconds ago]:

Adding [(t) seconds ago] to a clause gives you a match from the past.  (This is per-clause -- any other clauses in the query are in present tense unless otherwise specified.)  We've been able to do "[(n) ticks ago]" for a long time (and still can), but it's not very useful for n > 1.


Histories:  With (t) seconds of /history/

    With (2) seconds of /history/ for /p/ is a "page", /p/ has title /title/:
        for _,matches in ipairs(history) do
            log(matches.t, #matches)
        end
    End

This is like a two-dimensional "all matches".  "history" here is an array of match-arrays, each representing an "all matches" at a point in time.  e.g.:
{
{ t=123.4 }, -- no matches at this time
{ t=123.3, { p="<Page 1234>", title="Jerry" } }, -- one match
{ t=123.2, { p="<Page 1234>", title="Jerry" }, { p="<Page 1235>", title="Spot" } }, -- two matches
}

This draws a sliding oscilloscope-like trace of a page's value:

    With (3) seconds of /history/ for (you) points "left" at /p or nil/, 
                                      /p/ has value /v or 0/:
        local t1 = history[i].t
        local points = {}
        for i,matches in ipairs(history) do
            points[i] = { x = t1 - matches.t, y = matches[1].v }
        end
        Wish (you) draws "path" with points (points).
    End

Being able to "query across time" has been a dream for a long time, and this is a step in that direction.  For a long time, I had gotten hung up on the syntax, considering things like "When /p/ has value /v/ [over the last (3) seconds]" which couldn't work.  The obvious-in-retrospect insight was that collecting matches over time represents an aggregation, and therefore the matches themselves need to be aggregated -- that is, it needs to be an "all matches".

There's a lot further we can go with temporal queries, querying for specific situations and events instead of just gathering a whole history and processing it.  But I think this is a good first step.


MISC

From PC to printer

I made another uploading tool, "From PC to printer".  Scan the QR code and take a photo or video, and it immediately prints out as a page.  This was intended for quick "realcam"-like documentation.


Special URLs

The main rule of "Downloading from URLs" looks like this:

    When /someone/ cares when URL /url/ is downloaded to local file /something/,
         /nobody/ claims URL /url/ is downloaded in a special way [converging with priority (-10)]:

This allows other rules to provide special ways of handing URLs other than the default curl.  Most of these are collected on a page "Downloading from special URLs", but "Realtalk URLs" also uses it.  Our special URLs currently include:

- realtalk URLs ("realtalk:xxxxxxx.jpg") referring to shared files by hash
- base64 ("data:image/jpeg;base64,....")
- local pathnames ("/tmp/xyz.jpg") mostly for backwards compatibility
- pathnames to the old "shared" folder ("shared/videos/hack-the-planet.mp4") which gets redirected to http://dynabulb7/Realtalk-2017/shared/videos/hack-the-planet.mp4 where "dynabulb7" is the site leader.

Rules should generally never need to inspect their URLs.  They can just be written like:

    When the image is /image/,
         (you) reads URL /image/ as local file /pathname/:

This will work whether the image is an internet URL, a realtalk URL, already a local pathname, or whatever.

A special case is playing video, where we want to stream the video from internet URLs, but want a local file otherwise.  Ask for a "streamable URL", which can be passed directly to ffmpeg:

    When the video is /video/,
         (you) reads URL /video/ as streamable URL /url/:


"Included" in C

If you are exporting a struct which contains a custom type, the C translator needs to know whether you expected that type to come from an Import or from an #include.  "Included" lets you specify the latter.

    #include <vulkan.h>
    
    Included vkImage, vkThing.
    
    Import mydot_t.
    
    Export typedef struct {
        vkImage image;
        vkThing thing;
        mydot_t dot;
    } mystruct_t;

    

Included types can be matched and claimed like a built-in type:

    Included vkImage, vkThing.

    When my image is /vkImage image/:
        Claim my thing is ((vkThing)thing).
    End

Currently, these are implicitly casting to/from void* -- it does not check if the runtime value matches the type.


Hash collision fix

rtfreeze uses two different hash functions, Murmurhash3 for "big stuff" like tables and strings which can cache the hash, and a quicker hash for combining those hashes into match signatures and such.  When you write

freeze(a)

you get the murmurhash of a.  When you write

freeze(a,b,c)

each of a, b, and c are murmurhashed, but those hashes are then combined with the quick hash.

A few weeks ago I discovered the rarest bird -- a crash in the reactor -- and spent a pleasant Saturday afternoon tracking it down to a hash collision in my homegrown quick hash, which upon further inspection turned out to be just completely broken and it's unbelievable anything has ever worked at all.  I replaced the quick hash with Murmurhash2.



Implementation

After all of the additions listed above, I believe the total code size and page count have gone down, because a lot of it superseded or consolidated existing features.  These are all single-pagers below.  "Histories" is also a single pager which implements both delayed clauses and full histories.  I also had a good time sweeping through Illumination Kit and Decoration Kit, dropping unused pages and consolidating and cleaning up the remaining.



NUC BIOS update

Most of our NUCs were running a very old BIOS, which I think may have been related to USB ports randomly not working.  I made a "Make NUC BIOS thumbdrive" page which downloads the new BIOS to a thumbdrive, and I updated all of the NUCs I could get my hands on.



Dynalamp plates

I laser cut new versions of the dynalamp head plate and base plate, which etch the parts list directly onto the plate.



The pdfs for the laser cutter are now officially stored on pages, thereby obsolescing our actual last Github repo ("dynamicland-mounts").  (Well, we still use Github as an offsite backup for memories.)  These pages might go in Installation Kit, or maybe a new "Lamp Kit" or something.