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

Postfix Application Operator #625

Open remexre opened 2 years ago

remexre commented 2 years ago

Sometimes there's Silver code like:

top.foo = filter(someFunction,
            filter(someOtherFunction,
              flatMap(idkAnEnvLookupOrSomething,
                myThings)));

but I find this slightly hard to read. With OCaml's |> operator (just let (|>) x f = f x), this can be written:

top.foo = myThings
       |> flatMap(idkAnEnvLookupOrSomething, _)
       |> filter(someOtherFunction, _)
       |> filter(someFunction, _);

which IMO is more "naturally readable."

Other prior art includes:

This feels like it's probably a 100-line extension; possibly a "good first extension." Finding overly-nested code and refactoring it is possibly less of a good-first-issue thing, but like convenience aspects, I think we should be fine with saying "use it when you touch code that you think would benefit from it."

RandomActsOfGrammar commented 2 years ago

While the first code block is somewhat hard to read, as someone who doesn't use the postfix application operator, I would have no idea how to read the second code block. It might make more sense to write it as a series of lets or locals to keep it readable for everyone.

Readability aside, I think it might be difficult to add this as an extension because of MDA. Unless I'm wrong about how binary operators are implemented, this doesn't have a marking terminal.

krame505 commented 2 years ago

Readability aside, I think it might be difficult to add this as an extension because of MDA. Unless I'm wrong about how binary operators are implemented, this doesn't have a marking terminal.

The Silver "extensions" are only extensions in the MWDA sense, most of them probably wouldn't pass the MDA without some significant refactoring. And regardless it is possible to refactor the host grammar putting infix operators in their own nonterminal, such that the operator is effectively the marking terminal for that grammar. So I wouldn't put the MDA as a reason not to do this.

remexre commented 2 years ago

lets

I think this'd require making lets act as a prefixy thing to look nice

top.foo =
  let one = flatMap(idkAnEnvLookupOrSomething, myThings) in
  let two = filter(someOtherFunction, one) in
  filter(someFunction, two)
  end end; -- <- sad!

(Is our translation for lets actually reasonable, btw? I think it would be less efficient than having the operator, right?)

locals

Unless we get "uncached locals," aren't these measurably less efficient?

They also make it less obvious that "this is an intermediate value of this one computation, not something that other things should be using."

readable for everyone.

idk, this feels like something we could put in the docs, assuming the characters can be found by search. Ideally we'd probably choose the OCaml syntax for searchability too; it looks like JavaScript might be adopting the same syntax for the same operator too; a SO question about this is actually the first thing I get on Google for "|> operator".

Readability aside, I think it might be difficult to add this as an extension because of MDA.

Plus, we could do the operator refactor where

syn assoc::Assoc;
data Assoc { left(), right(), none() }

syn prec::Integer;

syn elabOper::(Expr ::= Expr Expr);

closed nt Operator with assoc, elabOper, prec;
nt Expr_1, Expr_2, Expr_3;

nt ExprOpRHSes;
concrete prod opRHSesNil
this::ExprOpRHSes ::=
{}

concrete prod opRHSesCons
this::ExprOpRHSes ::= op::Operator   expr::Expr_3  tl::ExprOpRHSes
{}

concrete prod op
this::Expr_2 ::= lhs::Expr_3 rhses::ExprOpRHSes
{ forwards to ...; }

Then, the operator terminal would be the marking terminal.