Rendered at 12:04:42 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
layer8 16 hours ago [-]
The drawback is that building an AST now requires a symbol table and resolving imports, possibly performing type inference and whatnot. It constitutes a higher barrier for various types of tooling. You really want your programming language to avoid becoming context-sensitive in that way.
It’s similar for the human reader: The examples are only intelligible to the reader incidentally, due to the names used and some natural-text conventions. In the general case, you have a seemingly random token sequence where you have no idea what binds to what, without looking up the type definitions or having an IDE present the expression in some structured way again.
Furthermore, in typical code you don’t have the case of constant values so often. You’ll rather have things like:
nextYear thisMonth.previous() lastDayOf(thisMonth.previous())
Double.parse(speedInput) m/s
startPos to (startPos + length - 1)
Schedule contacts.select(contactId) inputForm.getDateTime()
owlstuffing 6 hours ago [-]
> The drawback is that building an AST now requires a symbol table
Well, yes and no. During AST building a binding expression resolves as an untyped polyadic expression. Only later during the compiler's type attribution phase does a binding expression's structure resolve based on the operand types.
var plumber = contacts.select(contactId)
var date = inputForm.getDateTime()
var building = findLocation(warehouse)
Schedule plumber on date at building
But, honestly, I can't say I personally use it that way ;)
How is this different from backtracking? You're doing a depth-first search over possible interpretations. The grammar is just expressed in the type system instead of usual spec formats.
Critiques in other comments are accurate. This is a tooling nightmare, but also probably a nightmare to read. Consider an expression like
2026 March 10 to 13
What's the binding precedence? Does this mean March 10 through March 13, or midnight to 1 PM on March 10th? I think this breaks down outside of trivial examples that are better achieved in other ways.
xyzzy_plugh 12 hours ago [-]
Isn't it obvious?
It's the range from 1773100800 down to 13.
owlstuffing 7 hours ago [-]
Author here.
Yes, technically this is a form of backtracking, similar to what a parser does. The key difference is that the search is drastically constrained by the type system: reductions are only attempted where the types actually support a binding operator. Unlike a parser exploring all grammar possibilities, this mechanism prunes most candidates automatically, so the compiler efficiently "solves" the expression rather than blindly exploring every syntactic alternative.
Here is the high-level explanation of the mechanism:
But the short answer is that it’s not parser-style backtracking over a grammar.
The Java parser still produces a normal AST for the sequence of tokens. What happens afterward is a type-directed binding phase where adjacent expressions may bind if their types agree on a binding operator. The compiler effectively reduces the expression by forming larger typed expressions until it reaches a stable form.
The algorithm favors left associativity, but since a type can implement the binding operator as either the left or right operand, the overall structure of the expression can emerge in different ways depending on the participating types.
So rather than exploring grammar productions, the compiler is solving a set of type-compatible reductions across the expression.
The meaning is therefore determined entirely by which types participate in binding e.g., `LocalDate`, `Month`, `Integer`, `Range`, etc. and which reductions they define.
If a competing interpretation exists but the types don’t support the necessary bindings, it simply never forms.
In that sense it behaves less like a traditional parser and more like a typed reduction system layered on top of the Java AST.
owlstuffing 2 days ago [-]
What if these were real, type-safe expressions in Java:
2025 July 19 // → LocalDate
300M m/s // → Velocity
1 to 10 // → Range<Int>
Schedule Alice Tues 3pm // → CalendarEvent
That's the idea behind binding expressions — a compiler plugin I built to explore what it would mean if adjacency had operator semantics. It lets adjacent expressions bind based on their static types, forming new expressions through type-directed resolution.
Information that might be of interest to someone here:
The formal name for the “empty” binary infix operator that gets implied in the AST when doing this, is the “juxtaposition” (or “juxtapose”, or “juxt”) operator. The implicit multiplication operator between `3` and `a` in the polynomial expression `3a + 4`, and the implicit function-application operator in the Lambda-calculus expression `f x y`, are both instances of an implied juxtaposition operator (with different semantics for it in each of the two cases, as befits each type of algebra/calculus.)
evanb 17 hours ago [-]
Mathematica has Infix [0], which expresses the adjacency with a ~ (because Mathematica reserves pure blankspace for multiplication). But it works fine to do eg. `"hello"~StringJoin~" world"`; I was always surprised we could only have the predefined operators in many other languages and we couldn't define our own.
This seems like a great attempt. I would be worried about how much parsing and backtracking might be required to infer the infix precedence in a totally general system (like garden-path sentences[1]) or actually ambiguous parse trees (which is cured by adopting some rule like right precedence and parens, but what rule you pick makes some 'natural language' constructions work over others).
Haskell supports user-defined operators (made up of symbols) and also lets you use functions in infix position by surrounding the name in backticks, e.g.
2 `plus` 3
rather than
plus 2 3
emptysea 10 hours ago [-]
In Postgres you can define custom operators via `create operator`[0]
Similarly, Agda has a well-typed mixfix operator syntax – you define a function like (_foo_bar_baz) and can automatically write "X foo Y bar Z baz". It does mean that the operator parser has to be extensible at runtime, but it's not a huge cost for a dependently-typed language.
ben_pfaff 8 hours ago [-]
Bjarne Stroustrup wrote a paper about adding overloading for the "whitespace operator" in C++, but in his case it was a joke: https://stroustrup.com/whitespace98.pdf
tgv 15 hours ago [-]
I don't think this is useful in complex situations/expressions. Structure has to be encoded in the same place as meaning somehow. Natural language does it by using an extraordinarily large set of signifiers. That's not feasible for a formal language.
You could of course affix all lemmata with structural
information, as free word order languages do, but that's introducing syntactic structure via the backdoor.
thesz 15 hours ago [-]
An old paper on the expressiveness of the programming languages [1] had to add an implicit binary operator into whitespace to make Haskell not be ten times more expressive than most imperative languages.
So, yes, it can be done and it was done. Yes, expressiveness rises. No, reading comprehension of such languages does not suffer. Yes, it has to have a lot of scaffolding.
jnpnj 16 hours ago [-]
Sorry for this sounds absurd, but with diffusion language models, who generate text non-linearly (from the few that I get, they relate terms without a simpler order), I wonder if new syntactic ideas will come up.
sayanarijit 6 hours ago [-]
I like the fend calculator that uses similar syntax.
ape4 16 hours ago [-]
Maybe with a Java string templates:
var myDate = MAGIC"2025 July 19"
vips7L 8 hours ago [-]
StringTemplates were withdrawn.
bawolff 14 hours ago [-]
I'm sorry, but that sounds like it would be a debugging nightmare when it doesn't work right.
etbebl 11 hours ago [-]
God I can't believe I'm gonna be that guy, but I feel like in some of these situations one should just forward the text to an LLM and call it a day.
measurablefunc 16 hours ago [-]
Congratulations, you reinvented yet another stack language.
antonvs 15 hours ago [-]
No, stack languages can’t achieve this as described.
If you added a function to the examples, you could do a few of them, e.g.:
2025 July 19 date
299.8 M m / s velocity
But even this breaks down when you get to something like “Meet Alice Tuesday at 3pm”. Sure, you could contort things to make it resemble the concept, but it’d be a stretch at best.
It’s similar for the human reader: The examples are only intelligible to the reader incidentally, due to the names used and some natural-text conventions. In the general case, you have a seemingly random token sequence where you have no idea what binds to what, without looking up the type definitions or having an IDE present the expression in some structured way again.
Furthermore, in typical code you don’t have the case of constant values so often. You’ll rather have things like:
Well, yes and no. During AST building a binding expression resolves as an untyped polyadic expression. Only later during the compiler's type attribution phase does a binding expression's structure resolve based on the operand types.
https://github.com/manifold-systems/manifold/tree/master/man...
> in typical code you don’t have the case of constant values so often.
Agreed. It's not really useful with inlined expressions:
but if you write it like: But, honestly, I can't say I personally use it that way ;)Initially, I wrote it as a science extension to Java: https://github.com/manifold-systems/manifold/tree/master/man...
How is this different from backtracking? You're doing a depth-first search over possible interpretations. The grammar is just expressed in the type system instead of usual spec formats.
Critiques in other comments are accurate. This is a tooling nightmare, but also probably a nightmare to read. Consider an expression like
What's the binding precedence? Does this mean March 10 through March 13, or midnight to 1 PM on March 10th? I think this breaks down outside of trivial examples that are better achieved in other ways.It's the range from 1773100800 down to 13.
Yes, technically this is a form of backtracking, similar to what a parser does. The key difference is that the search is drastically constrained by the type system: reductions are only attempted where the types actually support a binding operator. Unlike a parser exploring all grammar possibilities, this mechanism prunes most candidates automatically, so the compiler efficiently "solves" the expression rather than blindly exploring every syntactic alternative.
Here is the high-level explanation of the mechanism:
https://github.com/manifold-systems/manifold/tree/master/man...
But the short answer is that it’s not parser-style backtracking over a grammar.
The Java parser still produces a normal AST for the sequence of tokens. What happens afterward is a type-directed binding phase where adjacent expressions may bind if their types agree on a binding operator. The compiler effectively reduces the expression by forming larger typed expressions until it reaches a stable form.
The algorithm favors left associativity, but since a type can implement the binding operator as either the left or right operand, the overall structure of the expression can emerge in different ways depending on the participating types.
So rather than exploring grammar productions, the compiler is solving a set of type-compatible reductions across the expression.
For example:
reduces roughly like this: And if `Month` binds with `Range<Integer>`: can reduce as: The meaning is therefore determined entirely by which types participate in binding e.g., `LocalDate`, `Month`, `Integer`, `Range`, etc. and which reductions they define.If a competing interpretation exists but the types don’t support the necessary bindings, it simply never forms.
In that sense it behaves less like a traditional parser and more like a typed reduction system layered on top of the Java AST.
Details here: https://github.com/manifold-systems/manifold/blob/master/doc...
The formal name for the “empty” binary infix operator that gets implied in the AST when doing this, is the “juxtaposition” (or “juxtapose”, or “juxt”) operator. The implicit multiplication operator between `3` and `a` in the polynomial expression `3a + 4`, and the implicit function-application operator in the Lambda-calculus expression `f x y`, are both instances of an implied juxtaposition operator (with different semantics for it in each of the two cases, as befits each type of algebra/calculus.)
This seems like a great attempt. I would be worried about how much parsing and backtracking might be required to infer the infix precedence in a totally general system (like garden-path sentences[1]) or actually ambiguous parse trees (which is cured by adopting some rule like right precedence and parens, but what rule you pick makes some 'natural language' constructions work over others).
[0] https://reference.wolfram.com/language/ref/Infix.html
[1] https://en.wikipedia.org/wiki/Garden-path_sentence
You could of course affix all lemmata with structural information, as free word order languages do, but that's introducing syntactic structure via the backdoor.
[1] https://www.researchgate.net/publication/2743686_Are_Ours_Re...
So, yes, it can be done and it was done. Yes, expressiveness rises. No, reading comprehension of such languages does not suffer. Yes, it has to have a lot of scaffolding.
If you added a function to the examples, you could do a few of them, e.g.:
But even this breaks down when you get to something like “Meet Alice Tuesday at 3pm”. Sure, you could contort things to make it resemble the concept, but it’d be a stretch at best.