Inverse parentheses
21 points by edk-
21 points by edk-
Did you know that Python’s grammar has braces? You just don’t type them. The tokeniser keeps track of the indentation level and inserts special tokens when it changes.
Reminded me of that Fortran story:
An ingenious idea used in the first FORTRAN compiler was to surround binary operators with peculiar-looking parentheses:
+ and - were replaced by )))+((( and )))-(((
* and / were replaced by ))*(( and ))/((
** was replaced by )**(
and then an extra ((( at the left and ))) at the right were tacked on. The resulting formula is properly parenthesized, believe it or not. For example, if we consider "(X + Y) + W/Z," we obtain
((((X))) + (((Y)))) + (((W))/((Z))).
This is admittedly highly redundant, but extra parentheses need not affect the resulting machine language code. After the above replacements are made, a parenthesis-level method can be applied to the resulting formulas.
I wondered if the examples would be easier to read if the syntax was also inverted, to avoid seeing () with two different meanings. After replacing () with )(, I’m not sure that the examples are any easier to read:
> )1 + 2( * 3
1 + 2 * 3
> 1 + )2 * 3(
(1 + 2) * 3
> ))1 * 2(( + )3 * 4(
1 * ((2 + 3) * 4)
The (( + ) in the last example looks unbalanced. I’m just too used to seeing parentheses that face each other as grouped.
I do think the examples are easier to read after replacing () with []:
> [1 + 2] * 3
1 + 2 * 3
> 1 + [2 * 3]
(1 + 2) * 3
> [[1 * 2]] + [3 * 4]
1 * ((2 + 3) * 4)
I actually kinda like your first modification. It's not super intuitive initially, but it is easy to visually transform to the "natural" parenthesis order, which builds intuition for me.
Each place an inverted parenthesis appears, you simply move it to the other side of the number it appears next to, then close out the imbalances at either end. Demonstrating:
> )1 + 2( * 3
=> 1) + (2 * 3
=> (1) + (2 * 3)
1 + 2 * 3
> 1 + )2 * 3(
=> 1 + 2) * (3
=> (1 + 2) * (3)
(1 + 2) * 3
> ))1 * 2(( + )3 * 4(
=> 1)) * ((2 + 3) * (4
=> ((1)) * ((2 + 3) * (4))
1 * ((2 + 3) * 4)
actually supporting this in a parser alongside regular parentheses sounds like a nightmare though, so probably just best to have these be alternate characters, if included at all :P
the 1 + (2 * 3) example looks really strange to me because it groups the 2 with the excluded expression. If the intent is to exclude phrases, such that this would evaluate to (1 + 2) * 3, doesn't it make more sense to just put parentheses around ( * 3), so that it is isolated from the rest? It feels more intuitive that 1 + 2 ( * 3) would evaluate the 1 + 2 first.
Well, this was mostly recreational for me, and I guess I was specifically interested in the idea of a language where you use parens to identify groupings you don't want. Intuitive, no not really, but I found it entertaining.
I agree that 1 + 2 (* 3) is a bit more intuitive. I'm not sure how it scales up to larger expressions though—how would we encode (1 + 2) * (3 + 4)? The only thing that looks sensible to me is 1 + 2 (*) 3 + 4.
Oh! I misunderstood what you were going for a little bit. I was thinking of it like "do the excluded operation last," which is a bit different from "don't do the grouped expression first." With that context, grouping (2 * 3) does make more sense
I don't really understand what "anti-grouping" is supposed to do.
Is the idea that normally, the parens in 1 - (2 - 3) can be thought of as "making all the operators inside higher precedence than the operators on the outside", so anti-grouping "makes all the operators on the inside lower precedence than on the outside"? So anti-grouped 1 - (2 - 3) gives grouped (1 - 2) - 3, and anti-grouped 1 - (2 - 3 * 4) gives grouped (1 - 2) - (3 * 4) (anti-grouping makes the inner - and * lower precedence than the outer -, but the relative precedence of the inner - and * is unaffected).
Yes. Well, what I was originally thinking was that parens normally force grouping, so my parens should force ungrouping; that is to say you'd identify the subexpressions you didn't want, and the parser would find a way to break them all. But when I spent some time with a pen and paper going through that, it turned out not to work, requiring overlapping parentheses to express what should be simple things.
My next idea was to view parens as changing precedence, since that can be inverted without breaking things. But I'm still curious if there's a better approach.
Have you encountered Peano’s dot notation? https://blog.plover.com/math/PM.html
Only since I posted this. I've had a few emails about historical weird things I didn't know. I haven't got my head around the dots yet, but I feel like they can be viewed as weakeners too.
Also it seems I wasn't the first to think of using spaces to control grouping, although I'm pretty surprised anyone would do it in a serious project.
Reminds me a bit of periods in ~early 20th century maths notation, which I think was introduced by Peano, but I've definitely seen Russel and Whitehead use it in the Principia Mathematica. This is still grouping, technically, but it appears around operators, which imo gives it a bit of a "separating" flair. For example,
1 :+: 2 + 3 .+. 4 + 5
means
1 + ((2 + 3) + (4 + 5))
I've been using notation like this a bit for personal notes, and for otherwise parenthesis-heavy lines it's quite useful!
When I see parentheses in most editors, like the expression
(a + (b * c)) + d * e
I cannot help but notice:
d * e means (d * e), but how do I see it?b * c instead of (b * c)
This has been a problem for multiple decades.
Have you seen "bracket pair colorization" in VS Code?
I have considered what it would be like if a language prohibited all redundant parentheses. a + (b * c) would be an error and only a + b * c would be allowed. People who don't have all the precedence rules memorized would probably be confused reading this.
You could also go the other way: eliminate all precedence levels and require parentheses whenever switching from one operator to another. a + b + c could be allowed but a + b * c would not and you'd have to write either (a + b) * c or a + (b * c). I imagine that this would be annoying, at least at first. I'm not sure if one would get used to it over time. There would probably need to be at least some special cases because writing something like x.left + x.width > max as ((x.left) + (x.width)) > max might be a bit much for people to take. In some sense this feels like reinventing Lisp.