To {{ or not to {{, that's the question

I’m pulling out this as a side track from RFC: Form binding with signals - #2 by SimonMartinelli.

When talking about signals, I’ve recently been using constructs like this:

add(new TextField() {{
  bindValue(someSignal);
}});

This is an anonymous inner class with an initializer block which is thus run like a constructor would be run for a regular inner class. It’s roughly similar to this conventional code block:

var myField = new TextField();
myField.bindValue(someSignal);

I know that this pattern has been abused previously and is nowadays considered an anti-pattern. But I also see that it fits very well into the way of thinking that we want to encourage with signals.

First, you’re not supposed to directly use component instances after they have been created when using signal. Instead, state changes should be applied to signals and incremental updates applied using signal bindings. This pattern means that there’s not even a variable that you might accidentally use outside of the context where the component is initially configured (though you can still “leak” it by doing weird things in the initializer). An additional benefit is that you avoid some redundant code by not repeating myField. 10 times if you are applying 10 different settings to that component. The flip side is that you don’t have a natural way of “naming” the instance to more clearly define its purpose.

Second, the whole point of signals is to have declarative configuration for your components. One potential benefit of this is the same as with declarative markup languages: the structure of the component hierarchy can be apparent from the structure of the code. It’s relatively obvious that these two representations have the same structure. Reading is also quite natural since the code starts from the outside.

<div class="outer">
  <div class="middle">
    <span>Inner</span>
  </div>
</div>
add(new Div() {{
  setClassName("outer");
  add(new Div() {{
    setClassName("middle");
    add(new Span() {{
      setText("Inner");
    }});
  }});
}});

The regular Java approach does on the other hand lose this structure and makes you set up inner components first so that you have them available when setting up the outer components.

var inner = new Span();
inner.setText("Inner");

var middle = new Div();
middle.setClassName("middle");
middle.add(inner);

var outer = new Div();
outer.setClassName("outer");
outer.add(middle);

add(outer);

I’ve heard multiple arguments against the pattern. Those objections are indeed valid for general use but I would claim that they mostly don’t apply for the particular way of using that I’m considering.

  1. Developers are not used to reading code using this structure. Sure, but the same could also be said about Java features such as lambdas or records when they were initially introduced. Some Spring annotations are unfamiliar if you’re used to CDI. jOOQ and JPA look different from each other. And so on. But we certainly have a “weirdness budget” that we need to be conscious of.
  2. There’s some overhead from having more classes. Sure, but Java today deals with lots of classes much better than in the old times when classes were in their own separate “PermGen” area in memory that got full after a couple of redeploys. There’s also a lightweight class behind each lambda or method reference but most developers today use those without similar concerns.
  3. Might cause memory leaks with and implicit “outer this” reference that is captured in the inner class. Sure, but that’s not an issue when an outer component class defines its child components since those children should no longer be used when the parent is no longer used. Lambdas can also capture “outer this” references and that’s rarely mentioned as a reason avoid lambdas.
  4. Some static code analysers complain about this pattern. Yes, that’s an actual problem.
  5. Some code formatters put the second { on a line of its own with an additional indentation level. This makes sense if the class also has other members but it only leads to visual noise in this case.

For now, using the {{ pattern specifically in the context of declarative-like component configuration together with signals is an experiment from my side. It’s probably the least bad way of getting a DSL-like structure for component trees within the constraints of the Java language. We will eventually have to decide whether we want to have that as the official recommendation that we’re using in e.g. documentation and various official examples.

Before we get to making that decision, I would like to hear if there are any additional arguments, for or against, that would be useful to consider?

Here couple of thoughts:

  1. {{ is standard Java and does not require any additional library. This means, whether we recommend it or not, one can use this syntax if it meets the purpose / fits the code aesthetics you want.
  2. Once Signals API get more mature with all the components, this syntax indeed becomes more approachable.
  3. Static code analyzers: If their come to your nerves, usually you can mute the rule.
  4. Create custom formatting rule, so that second { is not getting its own line.
  5. If you do not like it, do not use.
  6. Even in the case we would decide to develop some totally different declarative syntax specifically for Vaadin, this syntax would be still there and can be used.
1 Like

First I wouldn’t use it because it’s not idiomatic Java. You will not find in courses or books.

Second because of the reasons already mentioned. Lukas Eder wrote about it as well Don’t be “Clever”: The Double Curly Braces Anti Pattern – Java, SQL and jOOQ.

I’d like to see some non-trivial example before making my mind. Real-life UIs have dynamic parts that are shown conditionally or blocks (components) repeated many times over - how would those look like using this syntax?

Another issue is the readability of the StackTrace:

public PersonsView() {
    add(new Div() {{
       setClassName("outer");
       add(new Div() {{
            setClassName("middle");
            add(new Span() {{
                setText(PersonsView.getText("abc"));
            }});;
        }});
    }});
}

produces

Caused by: java.lang.RuntimeException: null
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView.getText(PersonsView.java:69) ~[classes/:na]
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView$1$1$1.<init>(PersonsView.java:62) ~[classes/:na]
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView$1$1.<init>(PersonsView.java:61) ~[classes/:na]
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView$1.<init>(PersonsView.java:59) ~[classes/:na]
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView.<init>(PersonsView.java:57) ~[classes/:na]

Where as this:

public PersonsView() {
    Div outerDiv = new Div();
    outerDiv.setClassName("outer");
    Div middleDiv = new Div();
    middleDiv.setClassName("middle");
    Span span = new Span();
    span.setText(PersonsView.getText("abc"));
    middleDiv.add(span);
    outerDiv.add(middleDiv);
    add(outerDiv);
}

produces

Caused by: java.lang.RuntimeException: null
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView.getText(PersonsView.java:69) ~[classes/:na]
	at ch.martinelli.oss.registration.ui.views.persons.PersonsView.<init>(PersonsView.java:62) ~[classes/:na]
1 Like

I 100% agree that you shouldn’t use that pattern for generic things like initializing maps. The scope in this case is only setting up child components from the constructor of a component (or a helper method called from the constructor if you’re so inclined).

I believe I already addressed all three concerns raised by Lukas and showed why they don’t apply in this case, so I don’t see the point of that reference.

jOOQ is not idiomatic for database access.
But that’s not in itself a reason to avoid using jOOQ.

Most books don’t cover jOOQ.
But that’s not in itself a reason to avoid using jOOQ.

A jOOQ query looks unfamiliar if you’re used to idiomatic SQL.
But that’s not in itself a reason to avoid using jOOQ.

There are online posts about why the jOOQ way is wrong.
But that’s not in itself a reason to avoid using jOOQ.

Can we please discuss this idea based on the pros and cons in the suggested context rather that based on generic cases and what someone on the internet said 11 years ago?

add(new TextField() {{
  bindValue(someSignal);
}});

I’ll play the serde advocate again here. Does this syntax in any way add an extra round of tripwire around the serializability of a class? If yes → ditch it. If not → sure why not but maybe Vaadin does not want to be known as the framework that made java look like typescript.

I am a big fan of easy discoverability in an api or sdk, and putting in additional constructs on the api surface to make the developer experience dsl like so that with . and autocomplete i can navigate large portions of an api.

Such cases need a little bit of library support if you want to apply changes reactively based on signal value changes rather than by directly updating component instances in event handlers. The library support would be the same regardless of whether we optimize for {{.

This forum is maybe not the best place for sharing more complex real-world examples but here’s an isolated example of the signals way.

Without {{

ListSignal<Item> items = ...;
add(new UnorderedList(items, signal -> {
  var li = new ListItem();
  li.bindText(signal.map(Item::text));
  return li;
}));
add(new Button("Add item", click -> {
  items.insertLast(new Item("new"));
}));

With {{, the button would stay the same in this case since everything is configured with a shorthand constructor. The UnorderedList would be like this:

add(new UnorderedList(items, signal -> new ListItem() {{
  bindText(signal.map(Item::text));
}}));

Correspondingly for a simple conditional rendering case using bindVisible.

var mustAccept = Span("Must accept terms");
mustAccept.bindVisible(() -> accept.value() == false);
add(mustAccept);

vs

add(new Span("Must accept terms") {{
  bindVisible(() -> accept.value() == false);
}});

The stacktrace is indeed longer but I’m not sure if that’s a big problem in practice. In most cases, I would anyways just click on the first or the last line with application code, e.g. PersonsView.java:69, in the IDE and get to the right line of code.

We have exactly the same phenomenon also in various lambda-based configuration APIs such as the Elasticsearch query builder that was praised in https://blog.frankel.ch/elastic-java-sdk-design/.

Tough question perhaps but here goes anyway, no harm intended :

Could the fact that {{}} makes things more readable (and I do admit it really does) indicate a shortcoming in the design of how Signals have been interwoven into Flow?

Doesn’t change anything. If TextField implements Serializable, then so does all anonymous subclasses of TextField as long as MyView where you define that subclass is also Serializable.

I prefer to see it as the framework that makes Java as cool as TypeScript for frontend. The fact is that most frontend developers prefer TypeScript (or JavaScript) and I belive one big reason for that is the way you can define the component tree declaratively while still also mixing in real code in places where it makes sense.

1 Like

I see it from the opposite direction. The way we integrate signals makes it possible to get the benefits out of {{ (though it’s still an open question on whether we really should). Without signals, you anyways need some components in variables so that you can do e.g. submit.setEnabled(false) from some value change listener. Signals make those variables redundant which also opens the door for considering a structure like {{ that wasn’t really practical before.

With just signals but no {{ you get

submit.bindEnabled(someConditionSignal);

which is already better than having submit.setEnabled(false) in one corner of the code base and submit.setEnabled(true) in some other corner. The question is just whether we can make it even better.

First of all, jOOQ uses idiomatic Java. Btw. You can say the same things about Vaadin that you mention about jOOQ.

My main argument remains that developers don’t use it because they are either unaware of it or consider it an anti-pattern. That’s also the reason why SonarQube complains, and if you have SonarQube in the IDE, it can automatically fix the code. All my customers use SonarQube, and they will all complain about this syntax.

However, you can certainly recommend whatever you like.

Unawareness is something we would have to address in our documentation if we choose to use this pattern in our documentation.

SonarQube and other similar analyzers is indeed one of the more challenging aspects since there’s at the very least some friction for changing the settings and there might in some cases not even be up to the development team itself to decide which rules are active.

Most of the time, the team cannot decide on the rules because they are defined company-wide.

I usually have issues with Karibu Testing and _assert() when trying to convince them to add this as a valid assertion in tests, because they want to define it globally.

1 Like

You know I’m not the biggest signals advocate. I think they might be a worthwhile addition to the core framework and they might really help you to improve the state management within Vaadin. But I’m not sure it’s going to be a huge improvement for usability for developers.

Vaadin is advertised as Swing alternative because of their similarity in how you build your UIs with events, listener and so on… This should still apply in the “new world”. I want to be able to read my source code without digging into weird syntax things I haven’t seen except in “Don’t do it” or “hacks”.

Yes, the source code looks a little bit more “dense”… but the same could be applied with proper Builder Pattern which is missing and requested for years. Developers all over the world know this pattern and want to build UIs with it. Please don’t try to reinvent the wheel with his syntax sugar and focus more on a good way to define UIs with either Java Builder and Signals or a declaritive way we have seen previously in V8 with Custom Layout.

Edit: Can’t resist:

2 Likes

I think it’s important not to reduce readability of Java code, introducing a new pattern into the mix would go against that.
I think most developers using Vaadin Flow, do not want to be (or are not) experts in Vaadin, they want to be (or are) experts in Java, so they would feel more comfortable if Vaadin followed common patterns, rather than invent their own.

The best thing we can do, is not to make them think about “how do I do this best in Vaadin”, but them just doing it, because its familiar.
It would be beneficial even for junior Java developers to be able to look at Vaadin code and understand it, and to be able to fix a bug they got assigned.

Similar to how lambdas and method referenced scared and confused me initially, I think this pattern would produce similar results, since that’s not something you really learn anywhere, or rather - if you learn, you learn no to do it. Especially since its not something that is widely being adopted in the industry (as far as I’m aware) unlike lambdas / method references.

3 Likes