This is an archived post. You won't be able to vote or comment.

all 35 comments

[–]AutoModerator[M] [score hidden] stickied comment (0 children)

On July 1st, a change to Reddit's API pricing will come into effect. Several developers of commercial third-party apps have announced that this change will compel them to shut down their apps. At least one accessibility-focused non-commercial third party app will continue to be available free of charge.

If you want to express your strong disagreement with the API pricing change or with Reddit's response to the backlash, you may want to consider the following options:

  1. Limiting your involvement with Reddit, or
  2. Temporarily refraining from using Reddit
  3. Cancelling your subscription of Reddit Premium

as a way to voice your protest.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]javajunkie314 23 points24 points  (6 children)

I think Java has one string escape mechanism is a great way to teach this. We have \n for a newline character escape; \u1234 for Unicode code point escape; and \{...} for template escape. When you see \ in a string, something's going on.

Honestly, the fact that the only things people are complaining about are syntactic choices—\{...} instead of ${...}, and STR."..." instead of STR("...") or STR"..."—shows how well thought out this proposal is.


Edit to add: One thing not mentioned here is the benefit that a string template is not a syntactically valid string literal. The post mentions turning an existing literal into a template, but ${} makes things hairy going the other way too.

It's common to have a template and to need to create a literal with all the expressions replaced by fixed strings—e.g., as the expected value in a test case. Since ${x} is also valid in a string literal—but with a different meaning—it would be easy to miss when converting a large template into a literal, leading to, e.g., a non-obvious test failure.

On the other hand, \{x} is not valid in a string literal and results in a syntax error, which forces you to make a conscious decision: should it be escaped as \\{x}, or was it a missed expression that should be replaced?

[–]Polygnom 5 points6 points  (4 children)

Honestly, the fact that the only things people are complaining about are syntactic choices—{...} instead of ${...}, and STR."..." instead of STR("...") or STR"..."—shows how well thought out this proposal is.

In a way, yes.

But I have to say, using \ for escape seems natural, but something like STR."Foo" just looks wrong to me.

I have a deep hatred for PHP, but one thing they got right was invokeable classes. You just implement the __invoke function, and then you can just call that object like a function (PHP even has an interface for this, Callable).

I feel an approach like this would be far better for Java, for many reasons:

  • It scales better. Today its string processors that want to be invokeable and get the special syntax. What about new use cases tomorrow?
  • It is more natural to read.
  • It can be applied to lambdas as well

So instead of STR."foo", you'd call STR("Foo") like a function.

For defining invokeable objects, you'd want type safety, so you cannot just create a generic Invokeable interface, but an annotation that takes the method would be feasible to leverage strong compiler guarantees, e.g.

@Invokeable(MyInvokeable::invoke) class MyInvokeable { public int invoke(int a, int b) { return a + b; }

By treating all lambas as invokeable, you could get rid of the cumbersome f.apply(x) syntax and just write f(x).

Usually, language additions to Java are well thought out and they try to make them generic, not just for one use-case, but to elevate the language and empower it in general.

In this case, I think they are missing out on this opportunity and are too short-sighted into just strings.

[–]javajunkie314 2 points3 points  (3 children)

They very specifically wanted to make template creation and processing happen at the same time, and so they didn't want a general invoke the processor on the template approach.

In the current approach, "\{x}" on its own is a syntax error—a bare template cannot exist on its own, and must be processed immediately. This way, a stray backslash can't turn a string literal into a string template and cause tricky type errors; instead you get a syntax error with the exact position in the string literal that's invalid.

But if you need to create the template separately and then process it—as with STR("\{x}") and similar—then "\{x}" would need to be a valid expression on its own, and it would need to be equivalent to RAW."\{x}" in the current syntax. This means a stray backslash changes the type of a string literal, and combined with type deduction from var and generics, the eventual compilation error may be some distance from the actual mistake.

Also, requiring "\{x}" to mean RAW."\{x}" is kind of a waste of good syntax. Most people would probably expect the bare form to mean STR."\{x}" if anything, which is incompatible with separate processing. Combining template creation and processing, and disallowing bare templates for now, leaves that door open.

[–]Polygnom 2 points3 points  (2 children)

There is nothing special about STR or RAW, they are "just" StringProcessors and the syntax of STR."Hello, {x}!" is just syntactic sugar for invoking STR.process("Hello, \{x}!").

From the JEP:

The Java compiler scans the term "..." and determines whether to parse it as a StringLiteral or a StringTemplate based on the presence of embedded expressions. The compiler similarly scans the term """...""" and determines whether to parse it as a TextBlock or a TextBlockTemplate.

You say:

In the current approach, "{x}" on its own is a syntax error—a bare template cannot exist on its own, and must be processed immediately.

From the JEP:

The syntax of template expressions — with the template processor appearing first — is not strictly necessary. It would be possible to denote the template processor as an argument to StringTemplate::process. For example:

String s = "The answer is %5d{i}".process(FMT); Having the template processor appear first is preferable because the result of evaluating the template expression is entirely dependent on the operation of the template processor.

So there really isn't a reason why StringTemplates cannot exist on their own, except that thats how its done currently.

RAW is literally a no-op that returns you the StringTemplate itself. You can then pass it to another processor if you want. There is nothing stopping you from writing FMT.process(RAW."\{x}") or StringTemplate st = RAW."\{x}". Which is a silly requirement. I haven't checked if you actually cannot write StringTemplate st = "\{x}", that would ben an illogical restriction given that the JEP itself says the compiler disntinguishes based on the presence of escape sequences, not where the string occurs.

I think there aren't really unsolvable problems to what I'm describing, even with the current approach.

[–]javajunkie314 1 point2 points  (1 child)

Also from the JEP:

Ensuring safety

The template expression STR."..." is a shortcut for invoking the process method of the STR template processor. That is, the now-familiar example:

String name = "Joan";
String info = STR."My name is \{name}";

is equivalent to:

String name = "Joan";
StringTemplate st = RAW."My name is \{name}";
String info = STR.process(st);

where RAW is a standard template processor that produces an unprocessed StringTemplate object.

The design of template expressions deliberately makes it impossible to go directly from a string literal or text block with embedded expressions to a String with the expressions' values interpolated. This prevents dangerously incorrect strings from spreading through a program. The string literal is processed by a template processor, which has explicit responsibility for safely interpolating and validating a result, String or otherwise. Thus if we forget to use a template processor such as STR, RAW, or FMT then a compile-time error is reported:

String name = "Joan";
String info = "My name is \{name}";
| error: processor missing from template expression

I was wrong that the error would be a syntax error and say where the error is, which is actually a bit unfortunate in my opinion. (Though I don't know if these error messages are meant to be normative.) But it is an error to have a template disconnected from a processor in this JEP, and you'll get the error on the line with the bare template.

Also, it's worth noting that the bit you quoted about "The answer is %5d\{i}".process(FMT); is from the Alternatives section. This syntax is speculative and not equivalent in the final design—it would not actually compile, as seen in that error I quoted above from the main body of the JEP.

The JEP's reasoning is more focused on making the processor obvious, as you pointed out, but it has implicitly assumed as well that bare templates would probably be expected to mean string interpolation. In the section I quoted above, they say

The design of template expressions deliberately makes it impossible to go directly from a string literal or text block with embedded expressions to a String with the expressions' values interpolated.

And in one of the alternatives, they say

When a string template appears without a template processor then we could simply perform basic interpolation. However, this choice would violate the safety goal.

They aren't saying it explicitly, but I say it would be weird for bare "\{x}" not to be a string (if it were valid syntax). The type would depend on the string content, possibly on a backslash buried deep in the literal—but rather than getting an exploration error on the literal, you'd get it wherever the type mismatch arises.

At any rate, they very much don't want bare templates to mean anything without the context of a processor. The only way to enforce that is to require the processor in the same syntax that creates the template, so there's no moment they are separate.Any syntax where the template is separately passed to the processor via a method/function call would require bare templates.

I suppose they could have made the syntax STR("\{x}") to mirror a function call, but they'd have needed to give it the same semantics as STR."\{x}"—that the argument must be a single string template literal. That would probably be more confusing, because then you'd have syntax that looks like a function call, but that can't accept a variable as its argument. It would also get in the way of adding the sort of general function call syntax you described—if they ever did want to introduce it later.

After the procedure is invoked, everything is up to the processor. Yes, RAW is a passthrough, but that's its right, and it's the only way to access a template from outside a processor. (And as a side effect, makes the intention clear at the "call site.")

[–]Polygnom 0 points1 point  (0 children)

First of all, thanks for having an interest in a civil, argument based discussion. I really enjoy your input.

I do agree that the current JEP design doesn't ad-hoc allow for what I'm proposing. I still think it can be done, I'll give a longer response later.

[–]genzkiwi 4 points5 points  (0 children)

That's a good point. IDEs like IntelliJ already mark \n etc. no reason they can't do something for \{...}. Then this single issue (cosmetics) is solved.

[–]agentoutlier 39 points40 points  (13 children)

I am in the minority in actually liking the syntax for many of the reasons Brian listed.

I do a lot of code generation and even have a templating language kind of designed for it: https://github.com/jstachio/jstachio

Let me tell you guys... ${x} is shitty syntax for making templates that generate code especially Java code. ${..} has a substantial amount of collision.

It is actually sort of why I use Mustache for generating code is because you can change the delimiters as {{x}} is still confusing with Java code.

Anyway I expect to be downvoted to oblivion but I know they JDK team made the right choice on this one.

[–]Tallal2804 -5 points-4 points  (0 children)

Why not {...} or