melt-umn / silver

An attribute grammar-based programming language for composable language extensions
http://melt.cs.umn.edu/silver/
GNU Lesser General Public License v3.0
58 stars 7 forks source link

Unary operator for new #843

Open krame505 opened 3 months ago

krame505 commented 3 months ago

Previously my thinking has been that tree sharing should be the default over undecoration in most cases, and that most cases where undecoration is needed can be avoided by data nonterminals/nondecorated locals. Thus, new() is a bit more verbose than @ in part to encourage one to use tree sharing instead. Additionally, to the flow analysis new() is just a function, while @ has special semantics.

However in practice when doing the #751 refactoring in Silver, there are (so far) 971 uses of new, to 657 uses of @. Some of these probably do point to places where further refactoring is possible, but there many cases (e.g. dealing with Type, or in a strategy attribute rule) where undecoration is the right choice. So it might be good to give undecoration its own unary operator for the sake of ergonomics. Having some symmetry with @ for the two ways of turning a decorated tree into an undecorated one is also nice.

I'm undecided what the best syntax would be. The unused operator symbols in silver are ~ ` # ^ & | \ - maybe ~ or #? Another option is * by analogy with dereferencing in C, etc. This is also the infix multiplication operator, but that isn't an issue in C (and probably less so in Silver?)

RandomActsOfGrammar commented 3 months ago

I don't think that adding a new symbol is a good idea. Typing new(t) isn't that hard, and the name "new" gives some idea of what it is. Using ~t, #t, *t, or any other unary symbol means only the Silver-initiated can understand it. A related name is also easier for people to remember what it is after reading a definition once.

A symbol could be useful if there was a clear analogy to an existing, common programming construct, which I do not see. I see your analogy to pointer dereferencing in C, as both sort of unwrap the underlying information, but it is not a close analogy, and trying to use it to explain the syntax connection is going to be more confusing to new users than helpful.

krame505 commented 3 months ago

I don't think that adding a new symbol is a good idea. Typing new(t) isn't that hard

That was my initial thinking, too, but I'm second-guessing this after seeing the amount of clutter added when removing implicit undecoration. And going forward, undecoration will be a very fundamental operation in Silver, like attribute access.

the name "new" gives some idea of what it is. Using ~t, #t, *t, or any other unary symbol means only the Silver-initiated can understand it. A related name is also easier for people to remember what it is after reading a definition once.

Agreed, but I will note that the meaning of new is also not obvious without explanation - I think I initially assumed it was something like object instantiation in Java. The new function also won't be going away in any case - we can still use new in examples/papers if we think that is clearer when explaining concepts. It would be a non-issue for anyone who regularly works in real Silver code bases.

krame505 commented 2 months ago

If I recall correctly, in discussion with @ericvanwyk we settled on ^ as the undecoration operator?

To reiterate, I think the strongest reason to add an operator is that strategy attribute rules get somewhat gross and hard to read with new everywhere - e.g. from the testsuite:

partial strategy attribute simplifyFrac =
  rule on Expr of
  | add(div(a, b), c) -> div(add(new(a), mul(new(b), new(c))), new(b))
  | sub(div(a, b), c) -> div(sub(new(a), mul(new(b), new(c))), new(b))
  | mul(div(a, b), c) -> div(mul(new(a), new(c)), new(b))
  | div(div(a, b), c) -> div(new(a), mul(new(b), new(c)))
  end;

vs

partial strategy attribute simplifyFrac =
  rule on Expr of
  | add(div(a, b), c) -> div(add(^a, mul(^b, ^c)), ^b)
  | sub(div(a, b), c) -> div(sub(^a, mul(^b, ^c)), ^b)
  | mul(div(a, b), c) -> div(mul(^a, ^c), ^b)
  | div(div(a, b), c) -> div(^a, mul(^b, ^c))
  end;