2 ms·
> This isn’t parser-style backtracking How is this different from backtracking? You're doing a depth-first search over possible interpretations. The grammar is
by kennethallen 7mo ago
> This isn’t parser-style backtracking
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 7mo agoIsn't it obvious? It's the range from 1773100800 down to 13.
- owlstuffing 7mo agoAuthor 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: https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-ext#how-does-it-work 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: 2026 March 10 reduces roughly like this: (2026 (March 10)) → March.postfixBind(2026) // → LocalYearMonth → [retreat] // → no binding with 10 → March.prefixBind(10) // → LocalMonthDay → .postfixBind(2026) // → LocalDate And if `Month` binds with `Range<Integer>`: 2026 March 10 to 13 can reduce as: (2026 (March ((10 to) 13))) 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.