1. 12
  1.  

  2. 4

    This has a lot of overlap with ideas I’ve been working on independently (like small computing, composable GUIs, implicit over-the-network message passing on cluster computers, and stack based languages with visible state). Presumably Don & I have both been paying a lot of attention to Alan Kay :). Glad there are fellow-travelers around – for a long time it was easy to believe I was the only Xanadu/Smalltalk/ZUI guy around!

    1. 5

      I really like the “tentative guidelines for composable uis” post. Going to save and reflect on that a bit.

      I’ve been working on a live-codeable interactive environment inspired by Self/Smalltalk etc., but using a Lua prototype-inherited scopes system (where code is “eval’d” in reified environments that can inherit from each other). After some iteration on such things I’ve realized it helps to have a concrete use case (allows testing, motivation, and empathizable communication with other people), but at the same time the choice of good use cases is important: if I choose a “make boring CRUD apps” use case, it basically involves porting existing libs + concepts and limits experimentation – so I went with a ‘generative art tool’ use case. This seems to let folks that look at it challenge their current ways of thinking about “software development.” It can evolve from static to animated art, then simulations (thus allowing games) and eventually hoping for network collaboration etc. Here are some videos:

      https://www.youtube.com/watch?v=zDGzEUJscYE (making an art sketch)

      https://www.youtube.com/watch?v=5-mxbhHBFOw (making another sketch)

      https://www.youtube.com/watch?v=rRMeOGc1JLQ (slightly older, using it on the phone, you can see the browsing / inheritance here)

      As you may have noticed, here too there is a concept of ‘sending messages’ to the scopes – all a message is is some code to eval at the scope, and that’s how you program objects to begin with anyways.

      Some of the ‘composable UI’ stuff I’ve explored here is that like the .__tostring(...) metatable function Lua datatypes can support, the ‘console’ window will call a .__toui(...) metatable function on values you try to print in it (if defined), so that objects can provide their own renderers, you can set custom renderers for slots that you add to scopes (going to explore this soon for color picker widgets), …

      I’m trying to use terminology like ‘perform’ etc. and more other art/human oriented words to move this tool away from “software engineering as a career choice” style orientation, as I think some of your blog posts also touch on. ‘Mindstorms’ by Seymour Papert along with some other readings are fun to explore here…

      1. 2

        This is fantastic!

        Some of my earlier experiments in the composable-UI vein used Lua, but I found that it was easy to run up against both coroutine problems (lack of preemption) & limitations in the maximum number of identifiers in the global namespace. Your work here looks a lot more advanced than mine ever got.

        (My current prototype is in Io, but I discovered that I would essentially need to rewrite Io to get a working system, because it stopped being maintained years ago & has problems with its speculative execution based thread planning.)

        1. 3

          Yeah def. understand the global identifiers thing – in my prototype above globals are by default written to the ‘current object’ and scopes can inherit, so it sort of works like process environments in UNIX. You can really bend Lua to your will a lot – I do it by setting the metatable of the environment that code is eval’d in.

          Thanks for the nice words. :) I really like your writings so will be digging in there more. Definitely feels like you’ve thought about this stuff a lot and there’s good overlap. Will update you as I make more progress on this. Let me know if / when you have any more sources for me to grok!

          1. 2

            There’s a group of people interested in the subject of composable UIs, hypertext, and utopian attempts to fix what’s broken about computing as a whole, over on mastodon. Most of my discussion happens there, & a lot of what I write on Medium is a refined version of discussions I have there. You might find that stimulating – I don’t represent the views of the whole community, which overlaps with the generative art scene.

          2. 1

            (My current prototype is in Io, but I discovered that I would essentially need to rewrite Io to get a working system, because it stopped being maintained years ago & has problems with its speculative execution based thread planning.)

            I didn’t realize Io wasn’t maintained anymore! That makes me sad. By happenstance, I’d looked recently and found the repo itself to be quite active, but it does mostly look like keep-it-going maintenance, not heavy work. Ah well. I recall it having its own pretty cool UI toolkit back in the day, too.

            That said, would you mind explaining a bit about the speculative thread planning? I was just an undergrad last I used it, and I’d thought Io had a pretty normal cooperative threading system; this makes it sound like I really misunderstood something pretty cool, but I can’t find much (any?) info about this on the language site. (All I could find was the note, “The Scheduler object is responsible for resuming coroutines that are yielding. The current scheduling system uses a simple first-in-first-out policy with no priorities.”)

            1. 1

              There’s some kind of complicated heuristic for determining whether or not coroutines have already exited, when determining whether or not to transfer control to them. I ran into false positives with regard to that behavior, which were not entirely reproducible. I asked about the behavior in the irc channel, & was told that this was a known bug with the scheduler system, and one of the reasons active development was halted – the author didn’t think he could get the behavior right in C, if I understand the history correctly.

              I started implementing a new version in Go (a language I don’t know, but one that has support for channels and real multithreading built-in). This should allow me to more easily make it support a smalltalk-style image-based format with a history & support for transactions & rolling back execution, too, so it’s a general win. (Plus, since I don’t need to keep full compatibility with Io, I can break that compatibility if it makes it easier to make my composable UI system – the important bits are message passing, multi-threading, a prototype-based object system, and a simple syntax with few keywords to memorize, all of which can be preserved.)

        2. 2

          Hell yes on composable components! Great articles, thanks!

          Here’s the money shot from an HN article I just posted about that:

          Valerie Landau interviewed by Martin Wasserman

          Q: Do you have any last minute comments or observations about him to finish up. Or a good anecdote?

          A: I think – I wanted to say one thing that Doug told me many years ago. And this is really for the software developers out there. Once, this was in the 90’s. And I said, Doug, Doug, I’m just started to get involved with software development, and we have this really cool tool we’re working on. Do you have any advice, about … for a young software developer. He looked at me and said:

          “Yes. Make sure that whatever you do is very modular. Make everything as module as possible. Because you know that some of your ideas are going to endure, and some are not. The problem is you don’t know which one will, and which one won’t. So you want to be able to separate the pieces so that those pieces can carry on and move forward.”

          https://news.ycombinator.com/item?id=17121629

        3. 2

          And the source code is available from Don Hopkin’s webpage.

          1. 5

            Alas that’s only the binary distribution of the HyperLook runtime and SimCity. The PostScript source is obscured by tokenizing it as binary and stripping the comments unfortunately. And I deeply regret the actual HyperLook source code for HyperLook is lost to me in the sands of time (unless Dug or Arthur has it on a tape somewhere), although I still have the SimCity sources.

            I scanned all the manuals for older versions of HyperNeWS, the entire HyperLook manual (it was pretty big), and the SimCity manual, which we made in the NeWS version of FrameMaker (aka PainMaker: it’s RIDDLED with FEATURES!) Links to those are at the end of the article.

            i’d love to run it again in an emulator (it’d run faster than ever I bet) and make some screencasts of demos, but my SS2 is in storage in the US. If somebody could please give me some help getting X11/NeWS to run on a SparcStation emulator I’d buy them a lot of beer or whatever they needed to tolerate raw unshielded doses of early Solaris. I have tried but haven’t been able to find the right images and get them to work.

            Best yet would be to run a SparcStation emulator in the browser, then anybody could actually run the original version of HyperLook and SimCity! Has anybody done that and configured it to boot up a version of Solaris that runs OpenWindows on a dumb color framebuffer? That would be fine!

            1. 1

              Good to know!

            2. 2

              Note: My article under discussion is still a rough draft! Arthur insisted I take the focus off him and focus it on HyperLook itself, and I decided to widen it.

              Now I’m describing how three exemplary apps, SimCity, Cellular Automata Machine, and Happy Tool, plug together and interact with each other in the HyperLook environment. How it uses message passing, delegation, prototypes, interface editing, property sheets and controls. And how structured PostScript graphics, data and code (PostScript data is polymorphic like JSON, and homoiconic code like Lisp) are the keystone that everything pivots around.

              Here’s some more stuff about the NeWS programming environment, the PSIBER Space Deck, a visual PostScript programming and debugging environment for NeWS that I made early at UMD HCIL:

              https://medium.com/@donhopkins/the-shape-of-psiber-space-october-1989-19e2dfa4d91e

              There’s also a big part missing from the HyperLook article that I have’t put in yet:

              A video tape demo on youtube of HyperLook, SimCity, the Cellular Automata Machine, PizzaTool, RasterRap, which I’ve transcribed and made screen shots, and will make into another article.

              Unfortunately the video itself is terribly compressed, and I haven’t had a chance to set up the equipment to re-capture it from video tape.

              But the illustrated transcript I’ll post soon will be easier to follow than the video, because it was recorded in the Exploratorium so there are kids screaming maniacally and people laughing uproariously in the background! ;) It was a pretty insane demo and the camerawoman almost dropped the camera, but I’m glad I got it all on tape! (Including the HappyTool back-story!)

              HyperLook Demo

              https://www.youtube.com/watch?v=avJnpDKHxPY

              Demonstration of SimCity running under the HyperLook user interface development system, based on NeWS PostScript, running on a SPARCstation 2. Includes a demonstration of editing HyperLook graphics and user interfaces, the HyperLook Cellular Automata Machine, and the HyperLook Happy Tool. Also shows The NeWS Toolkit applications PizzaTool and RasterRap. HyperLook developed by Arthur van Hoff and Don Hopkins at the Turing Institute. SimCity ported to Unix and HyperLook by Don Hopkins. HyperLook Cellular Automata Machine, Happy Tool, The NeWS Toolkit, PizzaTool and Raster Rap developed by Don Hopkins. Demonstration, transcript and close captioning by Don Hopkins. Camera and interview by Abbe Don. Taped at the San Francisco Exploratorium.

              EDIT: Just finished a big round of editing, please try again if you like! Next I do the transcript.

              Here’s a recurring theme explained:

              The Three Axis of AJAX, Which NeWS Also Has To Grind!!!

              NeWS was architecturally similar to what is now called AJAX, except that NeWS coherently:

              1. Used PostScript code instead of JavaScript for programming.
              2. Used PostScript graphics instead of DHTML and CSS for rendering.
              3. Used PostScript data instead of XML and JSON for data representation.

              We will return to these three important dimensions as a recurring theme throughout this article:

              The Axis of Eval: Code, Graphics and Data.

              https://en.wikipedia.org/wiki/NeWS#Design

              1. 1

                How was postscript as a fit for this kind of application? For instance, was it easy to add object encapsulation & message passing? Was it straightforward for users to modify running code?

                PSIBER looks like it contains interesting ideas with regard to making internal state visible, but I worry about the learning curve of stack languages for this purpose. I see the killer application for a composable UI system like this as the dissolution of the user/programmer distinction, easing people into casual programming the way that the unix command line eases people into casual shell scripting. A key part of that is having the language be something that a user can fit entirely into their head, but also something a fairly non-technical user can absorb through context. Stack languages, when the stack is not visible & unambiguous during iterative development, present something that must be understood and mentally simulated during development – which means homework before coding even begins. (For this reason, in Kaukatcr, the data stack, call stack, and dictionary are all part of the visible structure. But, I don’t think Kaukatcr really achieves the kind of ease-of-adoption I’m looking for, either.)

                I don’t suppose you did any user studies of non-programmers learning to code via interacting with PSIBER & running applications?

                1. 2

                  I found it great! And adding message passing and encapsulation was super easy! Owen Densmore’s “Object Oriented Programming in NeWS” he presented at Monterey 86 Usenix Graphics Workshop showed how to do that in two pages of code!

                  Object Oriented Programming in NeWS, by Owen M. Densmore, Sun Microsystems. 1986 Monterey Usenix Computer Graphics Workshop. 1986. http://donhopkins.com/home/monterey86.pdf

                  This led us to look for a formalization of this style. A Smalltalk-like class mechanism seemed to fill this need. Just before our beta release, therefore, we decided to look for the extensions we would need to make to PostScript to support classes. Much to our surprise, PostScript could implement classes with no modifications! The secret is PostScript dictionaries.

                  NeWS’s object oriented PostScript dialect is based on SmallTalk’s model, and implemented with PostScript’s dictionary stack (which is purely dynamic scoping: just look up names in the dictionaries on the dictionary stack in order of precedence).

                  It was very straightforward to inspect and modify running code! PostScript is totally homoiconic (“not that there’s anything wrong with that” ;) – code is simply normal data like arrays (whose executable bit is set). And objects and classes are just normal dictionaries, pushed onto the dictionary stack in search order.

                  It supported multiple inheritance, which we used a lot.

                  Sidebar: But we weren’t as multiple-inheritance-happy as ScriptX was, whose collections (arrays, dictionaries, sets, etc) were classes that many parts of the system inherited from, like the containment and clock hierarchies and timelines (you call the array append method to add children to them), while in PostScript (as in JavaScript), arrays and dictionaries are built-in, primitive non-object types, that you can use to build classes and objects but aren’t inherited from directly themselves (so you call addChild to append children to them). What I mean is that in PostScript (JavaScript), dictionary (object) is not class that other objects INHERIT from, but it is the primitive data type that classes and objects are CONSTRUCTED from.

                  But to contrast with PostScript and JavaScript, ScriptX was designed from day 1 to be an object oriented system with multiple inheritance, so collections are first class objects that you can easily subclass and mix in to your own classes that like to contain things or act like maps, so all the normal iterating and filtering constructs and collection methods worked on them. So in ScriptX you don’t loop over a container’s children, you loop over the container itself, since doesn’t HAVE and array of children, it IS an array of children.

                  Flounder: I can’t believe I threw up in front of Dean Wormer. Pinto: Face it, Kent. You threw up ON Dean Wormer. -Animal House

                  The syntax for sending a message to an object was “/foo obj send”, where “/foo” was like (QUOTE FOO) in Lisp, a literal name. Executable names are looked up on the dict stack, and their values executed (pushed onto the execution stack). Literal names are just pushed onto the operand stack.)

                  “obj” resolved to some object (and could be pushed on the stack any way, you didn’t have to give it a name like “obj”, it just had to be in the right place at the right time).

                  And “send” took the name of a message (or an executable array), and an object. It first established the context by pushing all the target object’s dictionaries on the stack (the old version didn’t bother to pop the old object’s dicts off, but later versions did). Then it looked up the name on the new dictionary stack (or just used the executable array you passed in), and executed it. Then it restored the previous context.

                  Passing in an executable array to send instead of a message name was kind of an optimization shortcut, and formally it should be methodcompiled, but if you knew what you were doing (like you just wanted to send a sequence of consecutive message to the same object and didn’t want to pay the cost to ping-pong back and forth between contexts) you could just methodcompile it by hand and coalesce the messages into an executable array of executable names (and other parameters and operators if you wanted).

                  In this way PostScript was a lot like a dynamically scoped Lisp with macros!

                  You could also send the /promote message to an object with a name and a value as an argument, to dynamically define an instance variable. Or send an /installmethod message to an object with a name and a method. It would dynamically methodcompile the method into the object’s scope and install the method on the instance as a local method.

                  That is one of the important properties of a prototype based object system, that you can dynamically attach methods and properties directly to instances, and later undefine them when you want!

                  The layout and painting code would often lazily promote cached measurements into the instance on demand. There would be a “backstop” method in the class that computed an expensive value and used /promote to cache it in the instance, overriding the backstop method, then later like in /invalidate or /resize, other methods could clear out any invalid cached methods or properties when something changed that they depended on, so they’d be recomputed and cached back on demand.

                  NeWS objects didn’t contain “self” references to themselves, but there was a function called “self” that figured out what the current object was by searching the dictionary stack top to bottom for an instance dictionary with a “ParentDict” key (later optimizations put a shared “ParentDictArray” in every instance of a class, which was quicker for switching contexts and searching than following links).

                  So it wasn’t very optimal to call “self” all the time, but formally you were supposed to. There was a very simple “method compiler” (like a Lisp macro) that optimized sends to the same object like “/foo self send” (push literal /foo, figure out self, and send message /foo to it) into just “foo” (look up foo on current dictionary stack and execute it without switching context), and it resolved “/foo super send” references to a direct inline references in the code (like “/foo supersend” so no dynamic resolution was necessary.

                  Here is all there was to the (original version of the) method compiler (later versions may have been a bit more complex but not much more).

                  % Crack open the methods and fix for "super send" and "self send"
                  /methodcompile { % method parentdict => newmethod
                      10 dict begin
                          /superpending false def
                          /selfpending false def
                          /parentDict exch def
                          [   exch 
                              {
                                  dup /send eq superpending selfpending or and {
                                      pop pop
                                      superpending
                                          {parentDict /className get cvx /supersend cvx}
                                          {cvx} ifelse
                                  } if
                                  dup type /arraytype eq {parentDict methodcompile} if
                                  dup /super eq /superpending exch def
                                  dup /self eq /selfpending exch def
                              } forall
                          ] cvx
                      end
                  } def
                  

                  And here’s all there is to send, supersend and self (later send was fixed to pop the previous instance off the stack before sending and restore it when done, and object’s parentDict key was changed to parentDictArray, and I think send was eventually implemented as a build-in operator since we used it so much):

                  % Generic Smalltalk-ish Primitives.
                  % Send a message to an object.
                  /send { % <args> message object => <results>
                      dup /parentDictArray get {begin} forall
                      begin
                      cvx exec
                      parentDictArray length 1 add {end} repeat
                  } def
                  
                  % Send a message to super without popping myself.
                  /supersend { % <args> keywordmessage superclass => <results>
                      exch { 2 copy known {exit} {exch /parentDict get exch} ifelse } loop
                      get exec
                  } def
                  
                  % Put me on the operand stack.
                  /self {/parentDict where pop} def
                  

                  Here’s another great early resource about NeWS programming. But it doesn’t cover the later refinements like multiple inheritance and optimizations, like using dict-like canvases, events and processes (threads) directly as objects (for making the actual canvases on the screen, event interest templates and event managers, into objects themselves, instead of separate objects just referring to them):

                  The NeWS Book. An Introduction to the Network/extensible Window System. James Gosling, David S. H. Rosenthal. Michelle J. Arden. David A. LaVallée. Sun Microsystems. 1989. http://donhopkins.com/home/The_NeWS_Book_1989.pdf

                  1. 2

                    Fantastic! I hadn’t realized that postscript had first-class associative array support – that makes any prototype-based object system a lot easier.

                    It looks like message passing is an immediate call, as opposed to adding to an object’s message queue to be handled at task switch time. Is this accurate? Was there any kind of task scheduling with regard to how objects handled their messages? If not, did this ever cause real problems (like accidental forward loops that prevent execution on other widgets from proceeding)?

                    I’ll have to read the NeWS documentation. I had been vaguely aware of postscript based windowing for a while but I didn’t realize it had smalltalk-like features.

                    1. 2

                      Yes that’s right. NeWS also had events and threads and monitor locks for synchronization, but those were a lot heavier weight than sending messages.

                      The wonderful thing about NeWS was its air-tight event synchronization, that users could actually feel in their sphincters: when the system got busy and slowed like it always (still) does, you could be confident that mouse and keystroke events would be deliver perfectly in the correct order to the correct recipient, so you didn’t have to suspensefully freeze and hang on the edge of your seat after clicking or typing, then stressfully wait for the system to catch up with you, hovering with your mouse button held down while holding your breath and gritting your teeth, worrying it might miss an event or deliver it to the wrong window, making your asshole tighten up stressfully in trepidation, like how X-Windows or even modern Mac and Windows desktops still do.

                      I still experience this exhilarating stressful anal tightening effect every time I press Cmd-F in Medium to search while in edit mode, because if I immediately start typing my search string, the first several characters get inserted somewhere in the document, then it starts searching for the partial string and the cursor moves away so I can’t see where it dropped a turd, resulting in accidentally saving documents with titles like “uckingPie Menus” that I don’t realize until a day later.

                      NeWS event distribution was synchronous. A process would express “interests” in events it wanted to handle. An interest was just an event used as a template or pattern for specifying which events a process was interested in, and which handlers to use when they are received. Certain important interests like MouseDown whose handing might change the event distribution policy (by expressing other interests in MouseMove and MouseUp, opening or closing or moving windows around, changing the input focus, etc), could be marked as “synchronous”. That meant that when they were delivered from the device input queue to the NeWS global event queue, ALL distribution of other NeWS events was temporarily blocked, until the the synchronous event handler (either the receiving process like the global event manager, or some other process like a client event manager that it directly dispatched the event to) explicitly unblocks the input queue, finally giving the system permission to continue distributing events after any changes have been made (like closing a window or changing the input focus from the document to the search field for example). So there was a hard guarantee that if you typed a character that closed a window, the next character you typed would be correctly delivered to the window under the cursor at the time AFTER the window was closed, instead of accidentally being put on the input queue of the window about to close.

                      I really miss being able to relax confidently after I click the mouse or press a key, and not worry the event will get delivered to the wrong place at the right time. I’m sure it’s taken years off of my life by raising my blood pressure and blocking up my digestive system!

                      1. 1

                        It seems useful from the perspective of guaranteeing that user input & focus is totally determinate. Did it ever make having totally independent windows update simultaneously a problem (say, if one of the two had a much heavier load)?