You are viewing archived messages.
Go here to search the history.

Ivan Reese 2025-06-26 19:19:08

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.

William Taysom 2025-06-27 06:23:54

Tom Larkworthy nonchalantly drops one "insane" idea after the other.

Konrad Hinsen 2025-06-28 07:50:40

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!

Konrad Hinsen 2025-06-28 07:52:52

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.

Konrad Hinsen 2025-06-28 07:54:14

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.

Marek Rogalski 2025-06-28 08:28:18

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.

Konrad Hinsen 2025-06-28 13:13:48

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.

Konrad Hinsen 2025-06-28 13:17:44

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?

Konrad Hinsen 2025-06-28 16:19:52

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.

Tom Larkworthy 2025-06-28 16:25:47

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.

Paul Tarvydas 2025-06-29 12:23:57

@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.

Konrad Hinsen 2025-06-29 12:33:10

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.

Tom Larkworthy 2025-06-29 12:48:28

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 the Observable 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

Marek Rogalski 2025-06-29 12:48:30

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.

Tom Larkworthy 2025-06-29 12:58:15

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.

Paul Tarvydas 2025-06-29 13:24:50

Tom Larkworthy

... 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

Tom Larkworthy 2025-06-29 14:06:52

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.

Konrad Hinsen 2025-06-29 15:18:48

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:

Paul Tarvydas 2025-06-29 15:43:37

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).

Paul Tarvydas 2025-06-29 15:52:00

... 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 ....

Tom Larkworthy 2025-06-29 18:06:23

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.

Konrad Hinsen 2025-06-30 06:33:23

Wondering: would the Linda execution model be appropriate here? You get fan-out and parallel evaluation. But it's not lazy.

Tom Larkworthy 2025-06-27 11:56:36

Thinking about bsky.app/profile/interjectedfuture.com/post/3lsjxqmfoxa2l implying node-and-wires are mutally exclusive with ASCII code and also thinking about Paul Tarvydas dislike of functions. Also connecting it with my own ideas that a good notebook should offer "multiple views of the same thing".

Made me think: why is everyone stuck on the idea that there is one one possible representation? And I think Paul Tarvydas has a point, it may be the function calling paradigm. In my work, I don't have this problem because cells subscribe to other cells in dataflow, so multiple consumers of a computation is not a problem. cells pull their dependancies. Whereas function calling is "pushing" values. The problem with pushing is you can only have one target. But with pulling you can have multiple consumers from one source. functions is building out of 1-to-1 links, but pulling is 1-to-many inherently.

I don't think that argument is water tight coz, of course, its all Turing computable, but I do think there is something in there that function calling ends up naturally reducing computational reusability so that trying to do pub-sub communication patterns ends up way harder and buggier than it should so you shouldn't bother unless absolutely necessary. But if you build out of pub-sub exclusively then you don't see multiple views of the same thing as an unusual concept.

https://bsky.app/profile/interjectedfuture.com|@interjectedfuture.com: Nodes and wires are so attractive because it's legible in a certain context. The context is when you're a beginner and you want to see the in-between results.

Breaks down when you know syntax, when you can imagine the intermediates in your head, and when the program gets large.

Konrad Hinsen 2025-06-27 12:31:58

Another difference is that your unit of code is the cell, whereas the unit of code in PLs is the function (plus a few others). Combinations of cells can be represented by nodes and wires. Combinations of functions are a lot more diverse, e.g. using higher-order functions.

Matt Curtis 2025-06-27 13:58:00

Are there any examples of programming interfaces or apps that use a pub/sub-first paradigm?

Tom Larkworthy 2025-06-27 13:58:51

spreadsheets, reactive programming.

Tom Larkworthy 2025-06-27 14:00:34

the event listener pattern is pub sub, its not like you can't do it in PL, but if you chain a ton of event listeners together synchronously you get weird cyclical bugs so its kinda harder than it looks to be a good pub-sub system.

Jack Rusher 2025-06-27 15:34:56

Dataflow is definitely more natural for many situations πŸ’―

Vitorio Miliano 2025-06-27 18:25:35

And dataflow was originally represented in text, not visually!

Kartik Agaram 2025-06-27 18:51:43

One aspect of functions I haven't seen mentioned yet here is the lack of a locus. You can call a pure function from any number of places, and they'll never conflict. Each call occurs in a private universe thanks to stack frames. Whereas replacing them with nodes and wires gives them a place and so introduces the possibility of lots of new failure modes. Can multiple wires start from the same port, end in the same port? What happens if 2 inputs come in at the same time on this wire? Did input A mix with input B that had accidentally been sitting around for a long time?

Personally I consider pure functions to be dataflow constructs. I also think of them as pull constructs, though I understand how they're more push than FRP and so on.

Kartik Agaram 2025-06-27 18:55:17

Does Excel seem like pull? It's often storing a function call in a cell. The data flow nature comes from binding the output of a call to a cell/place, and the function call doesn't itself inherently seem to interfere with doing so?

Tom Larkworthy 2025-06-27 18:58:38

Yes excel is pull because once a cell has a value. Any other cell can depend on it. It's all in an easily accessible global scope.

When a normal function returns, that result it private to the caller. You have to call that function again in many places and it's not the same calculation each time.

Kartik Agaram 2025-06-27 19:06:55

In Excel too it's not the same calculation each time, depending on the args you hardcode into the call.

Excel does provide a structured space for global variables, it's true πŸ˜„ In a conventional programming language you have to deliberately assign the result to a global.

Tom Larkworthy 2025-06-27 19:16:37

It's is, every value is essentially memoization in excel. The cells remember their result. If you are talking about a function call inside an excel formula, then yes, that's a traditional function call, I am not talking about that.

Even variable assignment to a global scope in trad PL, you still need to decide when to pull for the latest state. It's not enough to make a variable global for a push system. To make PL push you have to register a callback.

Kartik Agaram 2025-06-27 19:20:32

Yeah, I was talking about the function call inside a formula. So I think we understand each other.

Paul Tarvydas 2025-06-29 13:09:29

And dataflow was originally represented in text, not visually!

Please expand on this point. Thanks. Vitorio Miliano

Vitorio Miliano 2025-06-29 18:17:12

I don't think we had graphical representations of programs before Sutherland '66 and GRAIL '69? But, dataflow programming existed before that. Sutherland built his thesis atop CORAL, from '64, which calls out "flow diagram" as a thing it might be good for. Morrison claims "late 60s" for flow-based programming, and wrote its first implementation (AMPS) in S/360 assembly language, decidedly not graphically. Bell Labs had BLODI in '61. Wikipedia lists a bunch of more recent ones, but also, as a development paradigm, can't you do a dataflow architecture in basically any programming language (but especially functional ones, so, LISP was '50s)?

Maikel van de Lisdonk 2025-06-29 17:20:34

Hi, we have a date for our new online FoC meetup : Wednesday 23th of July 18:00UTC ..this is the link for the event on luma : lu.ma/1d5mc44t .. and more information about the setup of our virtual meetups can be found here: futureofcoding.org/meetups (please read carefully if you want to demo/present) .. for next meetup we already have one confirmed guest so we're looking for2 more demo/presenter. Let us know in the chat here or contact me directly. Thanks!

πŸ“ FoC Meetup Β· Luma

Information about our meetup can be found here : https://futureofcoding.org/meetups