<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p><br>
</p>
<blockquote type="cite"
cite="mid:CA+0ynh8Grzn=7BUj_WHLTmTnYL8GwzxF-pc7+zfbmJDek6ha5g@mail.gmail.com">
<div dir="ltr">
<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>
</blockquote>
<p>True, perhaps that isn't enough to justify them. They do allow
some other interesting things perhaps later that may be useful.</p>
<p>First, you could subscribe on a combination of properties:</p>
<p> x.with(y).map(Point::new).subscribe(p -> ... get
points here ... );<br>
<br>
To make this easy to use, we'd want to make `map` null safe, like
`map` in `ObservableValue`, and you'll just have to use `orElse`
to deal with `null`s (or perhaps a `mapNull` variant I've seen in
some stream type frameworks).<br>
<br>
Then I realized there could be another potentially cool use here
for the `null` skipping behavior (but I'll admit it is a bit far
fetched, but perhaps it will lead to other ideas). What if I did
this:<br>
<br>
// initially (x, y) are (0, 10):<br>
x.with(y).map(Point::new).subscribe(System.out::println);<br>
x.set(null);<br>
y.set(null);<br>
x.set(10);<br>
y.set(0);<br>
<br>
Because how `map` would skip nulls, this would only print Point(0,
10) and Point(10, 0) ...<br>
<br>
As said, it is far fetched, and clumsy to do, but I've thinking
for a long time about adding some sort of transactional logic for
properties as well (and I do have some other solutions for that
already that I've been playing with) and this kind of
mini-transaction sort of popped up out of nowhere.<br>
</p>
<p>--John<br>
<br>
</p>
<blockquote type="cite"
cite="mid:CA+0ynh8Grzn=7BUj_WHLTmTnYL8GwzxF-pc7+zfbmJDek6ha5g@mail.gmail.com"><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" moz-do-not-send="true"
class="moz-txt-link-freetext">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">
<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" moz-do-not-send="true"
class="moz-txt-link-freetext">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"
moz-do-not-send="true" class="moz-txt-link-freetext">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>
</blockquote>
</body>
</html>