<div dir="ltr"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">You're right, I forgot about the existence of the Bindings class, and the helpers it has added for these cases. I barely use it since the addition of fluent bindings. That version is a lot more compact and a bit more workable.</blockquote><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The primary reason for that is that ReactFX had to introduce new property classes which are limited in how well they can interop with existing JavaFX code. With the fluent bindings additions the need for ReactFX is limited. That is not to say ReactFX doesn't offer anything interesting anymore :) </blockquote><div><br></div><div>I don't use Bindings a lot since fluent bindings and have basically dropped ReactFX since then too. ReacrFX goes very far and has a lot of options, most of them cover rare cases. It's still very good to have a ready implementation for all that.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Static imports are not really a plus for any argument in my view :) Compare AssertJ and Hamcrest to see what I mean.</blockquote><div><br></div><div>I very rarely use them outside of test code, but when a class does extensive work in the context of a service class (like Bindings and Collectors) I could see using them. Also, I use Google Truth (nothing against AssertJ) :) Interestingly, JavaFX has a dependency on Hamcrest.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">It is an extension on the fluent API, it doesn't introduce anything that isn't possible currently, just like `map` and `flatMap` didn't introduce anything new, nor did `subscribe`. It is intended to be a more discoverable, fluent and modern API, and to fill a gap where one has to go from the fluent binding model to a static helper class model as soon as you go from mapping just one source to needing two or more sources.</blockquote><div><br></div><div> The 'select` methods on Bindings were not compile-time safe and IIRC used reflection. I never used them. Subscribe made memory management easier and avoided sneaky memory leaks (which can still happen, just less often). There were very good reasons to include them. Here it's more a matter of ergonomics.</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Tue, Oct 28, 2025 at 10:28 AM John Hendrikx <<a href="mailto:john.hendrikx@gmail.com">john.hendrikx@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>
<div>
<p>Thanks for taking a look Nir, I really appreciate it :)</p>
<div>On 26/10/2025 13:56, Nir Lisker wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">
<div>When I need to combine observable values I usually do
something like:</div>
<div><br>
</div>
Bindings.createDoubleBinding(() ->
width.get() / height.get(), height, width);
<div><br>
</div>
<div>which is much less cumbersome than subclassing (although a
bit less performant I think). It works for an arbitrary number
of observables (including) observable lists/sets/maps too:</div>
</div>
</blockquote>
<p>You're right, I forgot about the existence of the Bindings class,
and the helpers it has added for these cases. I barely use it
since the addition of fluent bindings. That version is a lot more
compact and a bit more workable.</p>
<blockquote type="cite">
<div dir="ltr">
<div><br>
</div>
<div>Bindings.createDoubleBinding(() -> list.getFirst() /
height.get() * map.get("A"), height, list, map); // assume
this makes sense somehow</div>
<div><br>
</div>
<div>ReactFX, which is the go-to library for such extension, has
a 'combine' method that work like this:</div>
<div><br>
</div>
<div>Val.combine(height, width, (h, w) -> w / h);<br>
</div>
</div>
</blockquote>
<p>I'm aware of this, and it is something to be considered as an
addition. It is similar to the argument where there is a
`Subscription.combine` and `subscription.and`, one being static
and the other being a fluent method. I think however we shouldn't
be relying on ReactFX too much anymore these days (I haven't used
it in years now). The primary reason for that is that ReactFX had
to introduce new property classes which are limited in how well
they can interop with existing JavaFX code. With the fluent
bindings additions the need for ReactFX is limited. That is not
to say ReactFX doesn't offer anything interesting anymore :)<br>
</p>
<blockquote type="cite">
<div dir="ltr">To assess the proposal, I tried to write your JEP
examples with the current JavaFX.
<div><br>
</div>
Multi-stage chain (not sure how you mapped to a Point3D from a
Point2D and a number):
<div><br>
</div>
ObjectBinding<Point2D> point2d =
Bindings.createObjectBinding(() -> new Point2D(x.get(),
y.get()), x, y);<br>
ObjectBinding<Point3D> point3d =
Bindings.createObjectBinding(() -> new
Point3D(point2d.get().getX(), point2d.get().getY(), z.get()),
point2d, z);
<div><br>
Combining chains:</div>
<div>
<div style="padding:0px 2px">
<div>
<p style="margin:0px"><br>
ObjectBinding<Point2D> point1 =
Bindings.createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y);</p>
<p style="margin:0px">ObjectBinding<Point2D> point2
= Bindings.createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y);<br>
ObjectBinding<Line2D> line =
Bindings.createObjectBinding(() -> new Line2D(point1,
point2), point1, point2); // Line2D is not a JavaFX
class</p>
<br>
Using a default value:</div>
<div><br>
</div>
ObservableValue<Point2D> point1 =
Bindings.createObjectBinding(() -> new Point2D(x.get(),
y.get()), x, y).orElse(Point2D.ZERO);</div>
</div>
<div style="padding:0px 2px"><br>
</div>
<div style="padding:0px 2px"><br>
</div>
<div style="padding:0px 2px">Some observations:</div>
<div style="padding:0px 2px">Bindings returns a Binding rather
than an ObservableValue and also has primitive specialization
versions that have their unique methods (add, subtract...).
These methods, however, can be replicated with fluent bindings
and they also have the "known" subtle GC issue for the
intermediary values.</div>
</div>
</blockquote>
Yes, I don't think we should cater to primitive specializations.
Bindings tend to be high level enough (often eventually tied to a
UI) that it makes little sense to "optimize" these at the cost of 8x
more variants. The GC issues are often insidious, and my problems in
that area have largely disappeared with the addition of the fluent
bindings and subscribe API's. This is why I'm hesitant to use API's
from the Bindings class, and why I think FX should offer
alternatives in that area.<br>
<blockquote type="cite">
<div dir="ltr">
<div style="padding:0px 2px">Also, static imports can make these
calls less ceremonious: createObjectBinding(() -> new
Point2D(x.get(), y.get()), x, y);<br>
</div>
</div>
</blockquote>
Static imports are not really a plus for any argument in my view :)
Compare AssertJ and Hamcrest to see what I mean.<br>
<blockquote type="cite">
<div dir="ltr">
<div style="padding:0px 2px"><br>
</div>
<div style="padding:0px 2px">The proposal is more ergonomic
with its fluency for a couple of values, but I'm not sure it
solves enough problems that the current mechanism can't.</div>
</div>
</blockquote>
<p>It is an extension on the fluent API, it doesn't introduce
anything that isn't possible currently, just like `map` and
`flatMap` didn't introduce anything new, nor did `subscribe`. It
is intended to be a more discoverable, fluent and modern API, and
to fill a gap where one has to go from the fluent binding model to
a static helper class model as soon as you go from mapping just
one source to needing two or more sources.<br>
</p>
<p>--John<br>
</p>
<blockquote type="cite"><br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Sun, Oct 26, 2025 at
11:59 AM John Hendrikx <<a href="mailto:john.hendrikx@gmail.com" target="_blank">john.hendrikx@gmail.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">JEP:
<a href="https://gist.github.com/hjohn/611acb65769b68a845b8919c62a3e99a" rel="noreferrer" target="_blank">https://gist.github.com/hjohn/611acb65769b68a845b8919c62a3e99a</a><br>
<br>
Hi everyone,<br>
<br>
I'd like to propose an extension to the fluent bindings API on<br>
ObservableValue (map, flatMap, orElse) which were introduced
in JavaFX<br>
19 over 3 years ago.<br>
<br>
The API currently is very powerful when dealing with a single<br>
observable, but lacks support when dealing with multiple
observables. <br>
For example, let's say you want to compute a width/height
ratio. You<br>
could write this:<br>
<br>
ObservableValue<Double> ratio = width.map(w -> w
/ height.get());<br>
<br>
... but you'll quickly find that such an observable will not
update<br>
itself when height changes, only when width changes.<br>
<br>
The go-to solution for this is currently:<br>
<br>
DoubleBinding ratio = new DoubleBinding() {<br>
{ bind(width, height); }<br>
<br>
protected double computeValue() { return width.get() /<br>
height.get(); }<br>
}<br>
<br>
My proposal would extend ObservableValue with a new `with`
method that<br>
returns an intermediate stage that can be easily converted
back to an<br>
ObservableValue:<br>
<br>
ObservableValue<Double> ratio =
width.with(height).map((w, h) -> w /<br>
h); // yields a ratio that updates whenever w or h changes<br>
<br>
Or for example:<br>
<br>
ObservableValue<Point> point =
x.with(y).map(Point::new); //<br>
yields a Point that updates whenever x or y changes<br>
<br>
The intermediate stage would not be an observable value
itself. This<br>
limits the API surface, and makes this proposal fairly
lightweight and<br>
much easier to implement.<br>
<br>
Please see the JEP for the full proposal. I look forward to
your feedback!<br>
<br>
--John<br>
<br>
<br>
</blockquote>
</div>
</blockquote>
</div>
</blockquote></div>