I’ve spent the past month prototyping my codeCAD language. However, while I’ve made tons of zero-to-one-type progress (an EBNF grammar, parser, function definitions, evaluation, and numeric solving!), the current possible demos are all terribly underwhelming — think the game programmer’s “triangle with color gradient” or the hardware engineer’s “PCB with a single blinking LED”.
However, even once I’m further along I suspect the demos will be fairly underwhelming — I’m not one-shot prompting my way to production G-code or building a slick augmented reality, Minority Report UI.
Rather, I’m after a particular kind of software hygge: Loads instantly, doesn’t crash, and fits nicely in the hand.
The objective is a feeling, and there’s no point trying to convince people — either the software exists and evokes the feeling, or it doesn’t.
Pitching it feels as nonsensical to me as pitching the deliciousness of an unbaked cake (which is why you haven’t heard about my crunchy tiramisu).
Unfortunately, this perspective makes prototyping tricky: How much baking is required to test an idea?
For example: One idea I’m exploring is “bidirectional editing”, so geometry can be manipulated using either:
If you graphically drag a point around, the coordinates in the source code should automatically update. If you edit the source code, the graphical UI should automatically update.
A simple way to test this idea is to throw a <textarea>
in the UI that displays the corresponding source code.
But to me, that feels terrible because I never want to be coding in some janky, in-browser <textarea>
— I want to be working with source code in Emacs, with all of my familiar key bindings, color schemes, autocomplete, and decades of cozy practice.
That’s the core appeal of a textual programming language.
But doing this properly is an absolute boatload of work:
How much of this needs to be built to evaluate whether bidirectional editing “fits nicely in the hand”?
It’s a fine line balancing between the reasonable “this is a prototype, we can fix the awkward parts later” and the tautological “the idea is good if we ignore all the bad stuff”.
While I’ve toyed with some programming language-ish projects before (a microcontroller configuration solver and relational spreadsheet), this is my first time building a language starting from a grammar and implementing stuff like binding, function resolution, and evaluation.
While there’s tons of 101-level resources for “building your first LISP” and evaluating arithmetic expressions like “1 + 2 / 5”, I haven’t found as much that gets in the fiddly details of more complex language semantics.
The most useful book I’ve found is Nystrom’s Crafting Interpreters.
I roughly followed its approach to building a lil’ tree-walking interpreter, but with Instaparse for parsing and some of the data format and processing machinery from Clojure tools.analyzer.
So far I’ve punted on doing proper (whitespace and comment preserving) unparsing in favor of brute force canonicalizing abstract-syntax-tree -> string rendering, but I expect I’ll need to flesh that out along with LSP support sooner rather than later.
I considered replacing Instaparse with Tree-sitter to leg into the latter’s better performance and potential ecosystem benefits (the “universal formatting engine” Topiary; Combobulate structured editing).
However, I decided against it for now as I feel like it’s worth trying to grapple with the challenges myself using a familiar tool (Clojure), rather than picking up frameworks and hoping I can mold them into the to-be-determined experience I’m trying to provide.
I have been reading around to learn about unified approaches for building a language with first-party support for editor tooling — where the LSP engine isn’t a from-scratch duplication of the compiler/interpreter’s analyzer, but is rather integrated as part of a single codebase.
(Aside: While a language implementation and editor tooling may make different trade offs around, e.g., throughput, latency, and tolerance of malformed inputs, I feel like a unified approach is more sustainable for single-author art project language.)
Some prior art I’ve seen there:
the Gleam language, which is written in Rust and has first-party LSP support. If I’m reading the source correctly, it tracks comments separately from the abstract syntax tree and then merges them back together when pretty printing.
“Lady Deirdre is a framework that helps you develop front-end code analysis tools, such as code editor language extensions, programming language compilers and interpreters, and even new code editors.”
a formal / Haskell-ish take on parsing and unparsing with lenses
the .NET compiler platform (“Roslyn”) stores trivia (comments, etc.) on tokens in the syntax tree.
Anyway, if you know of any programming language implementations (or bidirectional editing work) that I should study, please let me know!
“If you accept the premise that squares are satisfying, square theory offers a unified theory for why crosswords are satisfying too. And if squares are fundamentally compelling, the crossword, in its recursively square structure, starts to look like an equally fundamental art form.”
A 2001 Panel on language design with Paul Graham, John Maeda, Jonathan Rees, Guy Steele.
Book Review: Nadia’s Antimemetics | Applied Divinity Studies
Panko Part 1: Electric (Resistance Oven) Boogaloo. Can I make crustless electric bread at home?
I enjoyed Derek Künsken’s Flight From the Ages And Other Stories
JP Monetta demos ClojureStorm. This is the first debugger I’ve found myself using regularly. It records every intermediate value created during code execution, which lets you not just step forwards/backwards in time, but also to search so you can say “this number I see in this JSON response, where did it come from?” and trace it back to the exact database call or whatever. There’s a gazillion Strange Loop talks and academic papers about this kind of thing, but this is a program I can actually use in my Real Life.
Cursorless is a voice-driven structural text editor
“The blank page problem is fixed; in the 10% of cases where I lack inspiration to begin, I have the bot produce something and let my hatred for RLHF prose inspire me: I cannot rest until I edit the slop away.”
Strong Inference: Certain systematic methods of scientific thinking may produce much more rapid progress than others.
Dr. Dobb’s Journal Interviews Jef Raskin (1986). “If a little clock ever appears on a computer of mine, I’ll shoot it. A computer is supposed to be fast.”
Ian Dunt on why governments in Britain and elsewhere can’t get anything done – and how to fix it
The fewer the merrier: The merits of unified land ownership
Design for 3D-Printing the most comprehensive single guide I’ve seen on designing functional parts for FDM printing