The most recent FoC Virtual Meetup was fantastic . Three great presentations covering a pretty wide range of the FoC spectrum. The recording is live right here. Thanks again to our presenters, and I'm looking forward to seeing everyone next month.
Tom Larkworthy nonchalantly drops one "insane" idea after the other.
Very good presentations indeed! It makes me regret even more that I missed it. On the other hand, Ivan Reese asked exactly the questions to Tom Larkworthy that I would have asked as well, so I didn't really miss any opportunity!
Question to @Marek Rogalski: Is your build system cross-platform? In a narrow (2025 Windows, macOS, Linux) or a wider sense (anything windows-y or unix-y) ? My experience is that platform configuration is the main reason why it's usually preferable to use an existing build system rather than roll your own.
And I really have to take a serious look at Lopecode. The presentation makes it appear very Smalltalk-y, but with a different execution model.
Heh, it seems that my experience is exactly the opposite π When I run projects they typically leave the platform configuration to the user. For example: "this library requires packages , Y and optionally Z, please use your OS's package manager or provide the paths through environment variables". Maybe it's a fault of the C/C++ ecosystem. I've never seen CMake download and configure Windows SDK or VS Developer Tools.
In terms of cross-platformness it's rather limited. I only make sure it works of github's images for Windows, Ubuntu, on my Gentoo machine, and for anybody who reports a problem.
Looks like you have tech-savy users! Which makes a big difference of course.
I hope that CMake will never download anything. But it can figure out which compiler options are required for which C/C++ compiler.
Automat is a strange kind of addictive. I just spent a few minutes trying to spill coffee over the floppy disk. Why would I want to do that?
Tom Larkworthy I am going through the Lopecode Tour. Just the right level for me! But I am fighting against a UI issue: the edit boxes that pop up automatically when I have the mouse on a tile of the notebook are (1) very irritating and (2) often cover what I want to read. When scrolling through the notebook, I always have to move the mouse around just to see what I want to look at next.
Yeah, still figuring out the UX. I also find it annoying. In the next iteration I will make it a tiny toggle for edit mode, so it's noticeable but unlikely to block the view until you need it.
@Marek Rogalski - is there a separate URL for your build stuff? Or, is one expected to pry it out of the Automat repo? I totally agree that build-ing needs to be heavily revamped. Our current build processes are rife with out-dated biases from the 1960s.
Tom Larkworthy - You use the words "microkernel" and "reactive". I'm trying to figure out whether there is a direct connection between Lopecode and PBP(0D). I am hampered by a general ignorance of Observable. Comments (from anyone) might help me come down the learning curve. And, comments / questions / brainstorming (from anyone) about interrelationships (or non-interrelationships) between these ideas would be welcome.
@Achille Lacoin - PLs and OSs are dead to me. It's not 1960 anymore and we need fresh ways to look at program development workflows. This looks quite interesting and fires various of my neurons regarding steal-able ideas.
Tom Larkworthy One more question that remains after going through the tour: what's the relation, if any, between cells and modules? Cells seem to be the same as in Observable notebooks. But the latter don't have modules, which in Lopecode are quite central.
the runtime has modules and modules have reactive variables. thats the low level programming model and the core runtime state of the mu-kernel.
Cells are an artificial construct of the higher level language of Observable JS. This is what you see a notebook comprised of. Certain special cells actually map to more than one variables. A "viewof X" is two reactive variables, one holding a DOM node (the control plane, the UI, called "viewof X") and an inner one, the data plane (the "X") which is the data channel. A cell that imports looks like "import {a,b,c} from "z"", which is 1 cell, but it creates a dynamic import + a variable for each alias in the import (a,b,c), so is actually 4 reactive variables in total for that example.
So the concept of cells is the front end language and not a runtime concept directly. We don't need to really stick to that, I currently do for simplicity but Observable's new canvas product, for instance, is still sharing the same runtime but has done away with the notebook and cell concept entirely.
So a notebook is a module (literally an ES module). The module is composed of variables in the runtime. Those variables are grouped in cells. So transitively, cells also belong to a module, but I think it would be more accurate to say cells belong to a notebook which is implemented as a module. There is a function called cellMap which will scan a module and figure out the variable groupings in the decompiler.
I have more notes here which I am slowly filling out with finer details, although its not very proof read and super well structurally thought out, its more for me to remember some of the stranger things I discover while stepping through the code.
π Bidirectional Observable JS Runtime Toolchain
Compilation, source to runtime variable(s) Compilation takes notebook source cells written in
Observable Javascript
and turns them into reactive variables for execution in theObservable Runtime
. A cell is usually compiled to one runtime variable, however, mutable variables are more complicated and are represented as three runtime variables. ObservableHQ does the compilation process as part of the hosted notebook experience but in this notebook we provide a way to do it in userspace. Decompilation, Runtπ How the Observable Runtime works
Living documentation A lot of Observable is open source and MIT/ISC licensed. Additional tools have been built by the community 1st party Observable JS parser Lezer Grammar for Observable JS Observable Runtime Standard Library Inspector 3rd party Access the runtime The Unofficial Observable Compiler Userspace Observable compiler and decompiler Notebook Distiller Single File Exporter How Observable works In this section I try to explain the main concepts, surfacing the runtime concepts as live variables you
Paul Tarvydas no, it's a rather bespoke build script. I use it in a couple projects but copy it over and change a little each time.
direct connection between Lopecode and PBP(0D) -- Paul Tarvydas
based on my reading of this. Yes! Its a very similar programming model! In Observable each named cell has 1 output and n inputs which are late bound together to form a dataflow DAG. You can update the code for any cell and only the downstream cells are reevaluated. I see PBP allows multiple outputs per part, and technically the Observable runtime could also do this but currently the front end language Observable Javascript does not manifest that semantic, its only one output per cell. Roughly speaking a Part is a Cell. Also Observable Runtime / Lopecode its all clientside javascript so the environment is very different, but there are is a lot of conceptual overlap.
π Towards PBP / 0D for Bash - Pondβring Aloud
2025-03-20
... I see PBP allows multiple outputs per part ...
brainstorming: From playing with electronics, I call multiple inputs "fan-in" and multiple outputs "fan-out". Fan-out seems to be eschewed by FP. Multiple inputs and multiple outputs in FP is an illusion created by the power of destructuring (one blob of data, containing many non-homogenous datums). Fan-out, IMO, means data coming at different times (like requests to servers and daemons). Function-based thinking rules this kind of thing out. For example, Morrison's FBP does not allow fan-out, mostly (I think) on the basis that it is mathematically difficult to express in typeset text ( /bin/*sh
took a stab at this using clumsy textual syntax like |2
and program-lets like tee
, but that clumsiness just makes the goodness go away)
Official website for flow-based programming
I think the clearest use case for fan-out is a switch (where the dataflow goes to 1-of-n mutually exclusive ways), which is missing from Observable, in electronics this is a tristate bus where a component decides which of n components its currently connected to (its dataflow is turned off to all but one). You can't build that kind of logic with one output value. However, if you consider a reactive variable to be the wiring of the ports in the PBD diagram, and not the parts themselves, then you can make parts out of "collections of ports". So in that sense, you don't need multiple returns values, you need to be able to construct higher order components out of these lower level reactive wires.
I guess important to understanding PBP is what are the semantics of notification. Probably there are massive differences in the approach on that detail. In observable the variable is a scheduled function, but that function can also be a generator, which means that when recomputed, the variable can emit a stream of values onto the dataflow graph, and thus trigger its dependants to recompute many times. I don't expect many reactive systems to have that fanout-over-time semantics. Its useful to have a gameloop defined like this, where the one gameloop is triggering dataflow ticks every animation frame autonomously.
Thanks Tom Larkworthy, that was very helpful. I hadn't realized that Lopecode modules were ES modules. That's one concept less to keep in mind! :satisfied:
This is actually a question, but, I don't know how to form it as a question. A scheduled function is just a tiny "process". A scheduled function can trigger other scheduled functions. A scheduled function "call" is a non-blocking "call" to some other tiny process. Can a scheduled function trigger itself? A non-scheduled function can call itself - that's called "recursion". But, it's not "recursion" when a scheduled function triggers itself. I call that "feedback". Are scheduled functions required to return a result to their "callers" (triggerers)? AFAICT in FP, this routing policy is enforced and has resulted in clumsiness like ".then". OTOH if a scheduled function is not required to return a result to its triggerer, then it's not a "function" it's a "procedure" with side-effects. (IMO, that's a good thing).
... fan-out: fan-out requires copying or, at least, copy-on-write - concepts that were not memetically available in the 1960s. Now, with abundant GC, this kind of thing is a no-brainer ....
A scheduled function can trigger other scheduled functions
Only indirectly though (in runtime), due to late binding. So a schedule function publishes a value to a named location. There might not be a consumer of that value at the time of publishing. Later on you might later add the code to consume it, so the delivery to the next schedule function might occur much later.
A synchronised function calling itself is recursion. A scheduled function could refer to its previous value, which is more like a fold over time. This is actually possible in observable by using the keyword "this", which refers to the prior value of the variable (docs).
When the definition is invoked, the value of this is the variable's previous value, or undefined if this is the first time the variable is being computed under its current definition. Thus, the previous value is preserved only when input values change; it is not preserved if the variable is explicitly redefined.
Some of your words imply scheduled functions are triggering other functions directly. This is not how it works in Observable runtime. The dataflow scheduler is like a trampoline that is managing invoking functions, and it manages gathering the data to populate the input args. The functions themselves jsut declare what inputs they want (what outputs of other functions they depend on). If those inputs do not exist, the function is not scheduled. If any of those inputs change, the function is called, and it emits a value, which is then used to chain onto other functions if the runtime deems necessary. The scheduled functions do not trigger each other! The runtime triggers them, and makes decisions about whether more functions need to be triggered or not based on the dependancy graph.
Wondering: would the Linda execution model be appropriate here? You get fan-out and parallel evaluation. But it's not lazy.