In my very-biased view, this looks a lot like what...
# devlog-together
g
In my very-biased view, this looks a lot like what I think of as "layering". I don't trust comments, so I want a way to make diagrams tightly bound to the code itself, i.e. generate code from diagrams (syntax == figures, not just characters). In my view, to get "layering" you need total isolation (encapsulation of data is not enough, you also need to guarantee that the control flow is isolated). To consider: the stuff inside of boxes is important, but, are (some of) the flows important, too? I.E. I see the arrow in the first diagram as a data flow and a "sequencing" thing - it kicks off the box to the right (the Main page) box needs to wait until it gets a signal to begin (I call that sequencing and timing). The 2nd drawing shows 6 boxes and no arrows. Does that mean that they can be viewed in any order, i.e. how are they different from "TABs" on the window? I think that I want a viewer/editor that gives me hierarchical TABs, or, clickable boxes. Double-click on a box and you get to view its innards. Make some other gesture and you go back. Does this mean that you need a "map" off to the edge of the window to show where in the hierarchy you are? The popular web browsers I've used flit really close to this flame, but, don't satisfy...
k
I assume you consider "layering" to be a bad thing. Totally valid criticism. But there are drawbacks in the other direction as well: • If you generate code from diagrams your diagrams now have to do double duty. They have to be a source of truth and also hide detail. • You tend to be stuck with one totalizing way of viewing a large program. There are no arrows in the second picture because it's a different kind of picture. Glossary rather than blueprint. To support multiple viewpoints in your approach requires checking them for inconsistencies, and showing inconsistencies in a nice way to the programmer so the programmer can understand what the compiler has found. These are good directions for research. But my doctrine is that I'm trying to do as little research as possible. My focus isn't to make everything perfectly clean and pleasant to use. My focus is to help people take control of their computers with the mature technology we have today. This is why I'm adamantly not creating a new programming language.
To elaborate on "glossary rather than blueprint," we often hit points in programs where you can't stage learning. You can't learn A without knowing of the existence of B, and vice versa. In these situations it helps to have just a todo list. You need to learn these things. It won't make sense at the start, but here's a bound on what you don't know yet. I hope this conveys that my primary audience isn't the author of a program. It's the reader. If the author has to do some extra work to engender trust, I'm ok with that. A program will always consist of more than code. Keeping the comments up to date is part of fostering the reader's trust. You can't blanket distrust all comments, I think you miss out that way. Different people put in different levels of attention to them. I'm sure my projects are hard for others to understand, but they seldom contain outright falsehoods in documentation.
k
I am pretty much pursuing the same goals as @Kartik Agaram, just in a different setting. In particular, I use plain old code plus different views to make the system more understandable. But I am also interested in using diagrams as the ground truth, replacing code, for parts of systems. The big difference it makes is about changing things. If there is an author who makes an effort to facilitate the work of the reader, that also means that the reader has less of a chance to become an equal to the author. And that means lower conviviality. If your ground truth is a diagram, then you know that there is nothing else but the diagram and its semantics that you need to understand. No view can make such a promise, it's always a projection. Of course, that's also what makes this idea hard to realize. I am not aware of much work on formal semantics of diagrams, nor of their understandability.
g
I assume you consider "layering" to be a bad thing. ...
Actually, it's exactly the opposite. I think that layering is vital. Our current PLs are just forms of fancified assembler. This is a metaphor that is at least as tired as that of desktops. The tired metaphor of assembler leads to overly-rigid beliefs about programming.
k
Ah, thanks for that clarification. Ok, now I fit it right with your past writings. So you think it's a step in your direction, but it doesn't go far enough. Totally agreed. @Konrad Hinsen I'd suggest that overly formal semantics can also drive people away. Which is not to say my current level of formality is precisely the sweet spot 😆 I do design my data structures as tables containing tables containing strings and numbers, etc. That likely drives some people away. But one major alternative to formal semantic guarantees is to keep things simple enough that the universe of possible things to understand is not very large.
k
@guitarvydas I was thinking about your layering of "computational circuits" (which is how I tend to think of your approach) yesterday when I was looking at my own notes on code and data hierarchies, and I was wondering how data fits into your scheme. As long as you only have a fixed set of atomic types (numbers, strings, ...) in your queues, everything is fine. But how about structured data? Every component must then include a definition of the data it expects as input or provides as output. It must also pass this data on to its subcomponents, but it may not expose their data type definitions at its own interface. This doesn't sound impossible, but I am not aware of any of today's programming language that would support such a scheme without laborious and possibly inefficient data conversions.
g
@Konrad Hinsen This is probably a dissatisfying answer... How do you do this kind of thing across vast distances? Say, a machine in Toronto, Canada speaking to a machine in Paris, France? I'm of the opinion that you don't do this. Uncomfortably, the answer is something like JSON. The concepts of Programming Languages and Data Structures were invented to appease the biases of the 1950s, wherein "efficiency" was vital and time-sharing was vital and memory-sharing was vital and memory-structuring was vital. Today's hardware is entirely different from that of the 1950s. Today's problem is how to program DPUs (Distributed Programming Units) instead of CPUs. Thinking along those lines leads to completely different answers than what we're used to (in fact, I currently think that we need to deprecate O/Ss, PLs, Programming Using the SICP Method (PUSM), data structures, etc. [Aside: note that the current trend is to offload real work from CPUs onto GPUs, anyway]). Of course, we can't just erase everything we've got and start again. The giants of the 1950s/60s/70s/80s built on what they had (vacuum tubes, transistors) and came up with something new (they didn't just glom stuff into what they had, they didn't just make better vacuum tubes by incrementally tweaking the concepts). I'm nipping away at reimagining our biases, for example, simply using boxes and arrows and HTML elements as syntactic elements for "programming languages for the 2000s" instead of just using 7-bit characters. I'm using what we've got and trying to think about it differently. We know a lot more about programming techniques than we did in 1950/60/70/80 - can we just rearrange what we've got and build something fresh? So, you're right, I don't know how to handle complex data structures, but, I don't care.
@Konrad Hinsen @Kartik Agaram Let’s see if I can say this in a way that seems more practical. The following is about as short as I can make it for now...Composing Multi-Node Systems https://programmingsimplicity.substack.com/p/composing-multi-node-systems?r=1egdky
k
I think we have wildly different assumptions of scale in mind. I don't care much about zillions of nodes, shared memory, cache coherence, distributing computation across a room let alone timezones. They're valid use cases, but designing for them feels like the tail wagging the dog, to my mind. A single computer today can do so much in a single thread. I'd always choose the programming model that provides user-defined data types over the one that provides nice concurrency properties. So your model has some nice-to-have properties, but the lack of user-defined types feels like a deal-breaker.
k
Thanks @guitarvydas for an exhaustive answer to an interesting question - but it was not the question I have. I am not so much concerned with data representation for storage and transfer, but with data models or schemas, and in particular with ensuring that there are no misinterpretations at the interface between two components. In my corner of the computing universe, data is the primary concern of most people. For example, I work a lot with protein structures, which are structured collections of collections of atoms plus lots of annotations at all levels of this tree. What I care about is being able to feed such data structures into processing pipelines, storing those that come out of it, and being reasonably certain that all annotations have been interpreted correctly.
g
@Konrad Hinsen @Kartik Agaram I argue that this is the same problem, but looked at from a different end of the telescope. The tail-wagging-the-dog approach is to nip away at the problem incrementally. ... more to come ...
k
Yes, this seems worth digging into more. Is your "fewer functions" quest dual to "lots of concurrency," so that the proposal is you can never have compound data structures without the ability to serialize them and so on, and those considerations are first class to how they're designed. To me serialization seems a subordinate consideration to say Lua's syntax for numbers, strings and table literals. I don't need to think about serialization because anything can be converted to say JSON after the fact. It seems to be an independent question and "good design" is grabbing opportunities to decouple design considerations at every opportunity.
k
In my courses on computational science, I insist on the difference between "data model" and "data representation". JSON is a representation, and as @Kartik Agaram has pointed out, any data can be serialized to JSON (or XML, or s-expressions, or ...). Choice of representation is a matter of convenience and efficiency. I am much more interested in the semantics part, the data model. I don't see (for now) how it could be part of the same problem.
g
I've been writing and rewriting further comments which have resulted in my veering off to record another article in slideshow form. I think that my gut-feel boils basically down to: (a) I think that it's orders of magnitude more important to figure out how to deal with composing systems of loosely connected nodes (e.g. layering) (b) I don't think you can get there from here by tinkering with better forms of type checking and what are essentially better forms of assembler called High Level Languages. I see the basic case as: you've got one Raspberry Pi connected to an Arduino by a wire. What's the best IDE for programming that system? I believe that programming languages are just I caveman IDEs for programming. The Borg went all the way with clockwork thinking using their "You Will Be Assimilated" technique on Star Trek, but it probably took them a millennium to figure it out. It's Science Fiction anyway. Interpolating more complicated forms of type checking and more complicated PL designs, like callback hell, sync/async, .then, is like rearranging chairs on the Titanic, IMO. FWIW, if you haven't seen it yet, Modern Programming Issues, esp. the last two slides at around 2:25
k
@guitarvydas While I disagree "in the small" with your claim that types are unnecessary, I do agree "in the large" that the current focus on PL and in particular type systems is not likely to make significant progress towards improving software development. I see CS academia stuck on type systems exactly like physics academia got stuck on string theory: an initially promising idea turned out not to be very fruitful, but had already acquired the critical mass to form a research community that was too big to dismantle.
k
I feel mostly impatient with this thread. So much blather of so little use. When you could have been writing interesting new programs to make your point. Probably just this cold I've had for weeks and weeks. But one point of evidence just occurred to me: APL discourages user-defined types. So yes, perhaps there is more in this world than can be explained by Konrad's and my theories.
g
[aside: type this into ChatGPT or your favourite LLM "what was linus pauling's approach to vitamin c?"]
Thanks for this insightful comment! I am now working on filling this gap...
k
@Kartik Agaram APL is a DSL for applied mathematics. It wasn't even designed for execution on a computer. Applied mathematics is perhaps the most concrete branch of mathematics. It focuses on concrete representations that have been chosen for pragmatic reasons (easy to handle in computation). Overall, that's a good model for programming, but transferring the idea to other domains is not a trivial task. In a way, APL is an illustration of what @guitarvydas proposes: a well-designed notation that reduces the need for abstraction. This is also aligned with @jonathoda's quest for more concrete computing, and his Subtext looks like another good example for a DSL for some types of tasks. So maybe our real problem is the dominating focus on general-purpose languages and universality.
g
Here's an attempt at making this stuff a little less unclear. See README.md. I've written about these examples before, but, I've never drawn them all together into one repo. An interesting side effect of all of this was that, in the process of creating this repo, I whipped up an LLM example and extended it into a double-LLM feedback loop in a matter of minutes (the
dpink
example) (the resulting output is actually enlightening at a philosophical level, to me, IOW the experiment was a "success" and needed only 3 Parts). The
rt
kernel involved more development work, but the LLM example shows how easy it is to create apps/experiments when the right abstraction is used.
k
I don't get it at all. I tried clicking around a bunch, I really did. There's some disconnect. I have no idea which way is up, what you're trying to accomplish, what your metric is for quality. It all looks like Rube-Goldberg machines. Like the arith example. Where is the actual conversion to anything?

https://github.com/guitarvydas/zddemos/blob/main/arith/arith-main.drawio.png

doesn't feel like the interesting part of a compiler to any single one of those languages. So what am I supposed to be looking at? Where is the algorithm, as opposed to the orchestration of coarse steps? Is dpink running a local LLM? Dispatching to an external LLM? Perhaps you should take me through just one of these examples. Show me what the dev experience is. If I use your approach, where do I spend most of my day? How do I make changes? How do I locate and fix problems? But then again, I just don't know. You've certainly tried showing a single example before. You're constantly seeming to respond to my suggestions and nothing helps. Maybe I'm just dumb or blind, and you should stop running around in circles for me 😓
g
I would guess that the "problem" isn't you, it's me not saying something. It would probably be helpful to me to walk you through one of these and have you ask questions and have you point out what I'm not saying. Let me know what suggestions you have on poking at this further (if interested)...
[fyi, this stuff looks definitely non-Rube-Goldberg-y to my eyes. In fact, Lua, Python, etc. code looks like clunky assembler to me]
k
You're comparing apples and oranges. On one side we have programs you have written. On the other side you're making generalizations about languages without any example programs. And the reference to assembler feels like an aesthetic judgement, but I'm not yet at the point where I have the luxury of judging aesthetics. I'm just trying to figure out how the human is supposed to read it. And there are certainly small Lua, Python programs where that's no problem at all. So yeah, it's probably a problem of presentation. It's not clear to me where the non-assembler parts are, given you're building these things out of one cartoon drawing and six different bits of what you call assembler in six different assembler dialects. Where is the insight? Am I asking what water is? 😅 Maybe we should do a call.
Focus on communicating what this B is before you try to compare it to mainstream A.
g
I'm certainly open for a call [if anyone else wants to join in, that would be OK, too, from my perspective]. I'm pondering what you've said. I can see that something is missing, but, I'm at a loss to say what it is. It's something about economy of expression, removing ice jams caused by worrying about too much detail at too low a level, something like that. I tried to poke at Claude 3.5 to figure out why Richard Feynman used Feynman diagrams instead of written equations and got no insights on how to say this differently. Continuing to use PLs based on 1960s graphics technology and only one way of looking at things (sequentialism) doesn't seem like a fruitful approach. To me, it looks like we're at a precipice, the kind faced in the 1960s by people holding bags of transistors, but different. [In the dpink LLM example and the Larson Scanner example, there are wires that feed back to previous stages. That's not "recursion", that's something different. Knowing that I was allowed to draw such feedback wires simplified my thinking about how to solve those problems. Assembler, and it's derivatives like FP, don't encourage you to think that way]. I feel that FP imposes too many semantic restrictions on thought. ... continuing to ponder ...
k
Yeah, that again does nothing for me. You keep flying off into airy generalities. But people will only trust the abstract once they have some grasp of the concrete. For example, from what I can tell, one major case you make for your approach is that it's more efficient. Things that don't depend on each other should be able to run in parallel, and that is only possible if there aren't accidental dependencies introduced by the tools. What you call epicycles. Well, if the major case is that it's more efficient, show me the most powerful case for how efficient it is. Or if you want to focus on some other benefit, show me a concrete working program and convince me it has that benefit. Pick one benefit, and thread it through from start to finish. Take me through a linear argument, I have no trust at this point to spend time clicking around on your stuff. I don't want to see multiple notations, I don't want to bounce between 3 different services, I don't want to read LLM slop. (Use LLMs by all means. I don't care how you generate your argument, and I don't want to hear about it either. It's your argument no matter how you arrived at it, and it wins or loses on its merits.)
None of us get to compare our notations to Feynman diagrams until someone else uses the notations to obtain publication-worthy levels of insight.
g
This demonstrates a microcosm of my problem. I wrote "... why ... ... used ... ... instead of written equations". I perceive this to be a question about process. Those words were taken, instead, to mean name-dropping and direct comparison of 0D to a specific famous artefact. The interesting bit is how the thought process worked to solve a hoary problem (observation: the technique involved abandoning equation-based notation instead of simply embellishing it). The question should be "what are the significant semantic concepts used in 0D that supplement current programming best-practices?" instead of "what do these wires mean?". [I've tried to state what those concepts are in several ways, many times, but, the simplicity of the concepts appears to make them ignorable]. Analogy: We don't need to know how to make better internal combustion engines just to drive cars. We do need auto mechanics, but, the majority of drivers don't need to know how to service their own automobiles at a fine-grained level. FP is like pistons (i.e. only a small % of what's needed to make an engine work). 0D is like the hood (or bonnet) that hides all of that detail. Computer Science has become like a course in automobile mechanics, albeit partial and very biased (analogy: flat-head screwdrivers only, the use of Phillips and Robertson screwdrivers and pliers is forbidden). Programming should be like driving. Programming is not what Computer Science has become. [Haha, if the question in the 1st paragraph above is correct, then why don't I simply address it? ... TBD ...]
k
But we're not talking as producer and consumer, we're talking as peer programmers right? If you want the world to adopt your way of building cars, you have to convince the mechanics. I didn't think you were name-dropping or anything like that. I don't think the way you are arguing is factually wrong or bad faith. I just think it's ineffective. The names are distracting rather than supporting the reader.
g
I wonder. The mechanics still haven't adopted spreadsheets, have they? I watched assembler programmers howl at how inefficient "C" was. Then they all retired and "C" was adopted. Hmm.
k
That's fair too. Perhaps you should clarify what level you want engagement on. That might help me stop saying unhelpful things.
g
Interesting food for thought - thanks. I thought that I knew who my intended audience was. I wonder if I should reconsider? Note to self: I'm describing how to do this not why to do this.