Re: Proof of concept for fluent bindings for ObservableValue

2021-10-07 Thread John Hendrikx

Nir,

I've created a new branch which contains all the changes which we've 
discussed so far.  It contains JUnit 4 tests (in backported form), and a 
reduced API.


However, the changes made in the sandbox did not fully compile due to 
package restrictions, and I had to make a bit more API public.


The issue is that ObervableValue and ObjectBinding are in two different 
packages.  LazyObjectBinding must subclass ObjectBinding and requires 
two new methods (isObserved and allowInvalidation). ObservableValue uses 
classes like MappedBinding which subclass LazyObjectBinding.


Making the **Binding classes package private means ObservableValue can't 
access them. Moving the binding classes to ObservableValue's package 
means that LazyObjectBinding cannot access the isObserved and 
allowInvalidation methods of ObjectBinding.


So, we have two choices (that I can see):

1) Make LazyObjectBinding and subclasses public so they can be accessed 
from ObservableValue's package. LazyObjectBinding still must be in same 
package as ObjectBinding since the isObserved and allowInvalidation 
methods are package private.


2) Make isObserved and allowInvalidation *protected* methods of 
ObjectBinding so that LazyObjectBinding can access them from 
ObservableValue's package (also move all the other new binding classes 
there).


I went for the second option in my implementation as this exposes the 
least additional API.  I also think that the additions to ObjectBinding 
are relatively harmless, and in the case of isObserved might actually be 
useful for debugging.


If we ever want to make LazyObjectBinding and its subclasses public, 
we'd have to move them to the binding package as they make more sense 
there, but as long as they're package private they can safely live in 
the same package as ObservableValue.


Please see here for a draft version. If you could do a quick check and 
you're agreed with the route I took to resolve the package access 
restrictions, I can submit this as a PR:


https://github.com/hjohn/jfx/tree/feature/fluent-bindings

--John

On 24/03/2021 22:49, John Hendrikx wrote:

I just wanted to draw some attention to a recent proof of concept I made
in this pull request: https://github.com/openjdk/jfx/pull/434

It is based on the work I did in
https://github.com/hjohn/hs.jfx.eventstream which is in part based on
work done in ReactFX by Tomas Mikula. The PR itself however shares no
code with ReactFX and is
completely written by me.

If there is interest, I'm willing to invest more time in smoothing out
the API and documentation, investigating further how this would interact
with the primitive types and adding unit test coverage (I have extensive
tests, but thesea are written in JUnit 5, so they would require
conversion or JavaFX could move to support JUnit 5).

What follows below is the text of the PR for easy reading. Feedback is
appreciated.



This is a proof of concept of how fluent bindings could be introduced to
JavaFX. The main benefit of fluent bindings are ease of use, type safety
and less surprises. Features:

Flexible Mappings
Map the contents of a property any way you like with map, or map nested
properties with flatMap.

Lazy
The bindings created are lazy, which means they are always invalid when
not themselves observed. This allows for easier garbage collection (once
the last observer is removed, a chain of bindings will stop observing
their parents) and less listener management when dealing with nested
properties. Furthermore, this allows inclusion of such bindings in
classes such as Node without listeners being created when the binding
itself is not used (this would allow for the inclusion of a
treeShowingProperty in Node without creating excessive listeners, see
this fix I did in an earlier PR: #185)

Null Safe
The map and flatMap methods are skipped, similar to java.util.Optional
when the value they would be mapping is null. This makes mapping nested
properties with flatMap trivial as the null case does not need to be
taken into account in a chain like this:
node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty).
Instead a default can be provided with orElse or orElseGet.

Conditional Bindings
Bindings can be made conditional using the conditionOn method. A
conditional binding retains its last value when its condition is false.
Conditional bindings donot observe their source when the condition is
false, allowing developers to automatically stop listening to properties
when a certain condition is met. A major use of this feature is to have
UI components that need to keep models updated which may outlive the UI
conditionally update the long lived model only when the UI is showing.

Some examples:

void mapProperty() {
  // Standard JavaFX:
  label.textProperty().bind(Bindings.createStringBinding(() ->
text.getValueSafe().toUpperCase(), text));

  // Fluent: much more compact, no need to handle null
  label.textProperty().bind(text.map(String:

Re: Proof of concept for fluent bindings for ObservableValue

2021-10-05 Thread John Hendrikx

I made the title more specific :)

--John

On 05/10/2021 17:58, Nir Lisker wrote:

Looks good. I assume we will add more bindings in the future like
conditionOn or anything else that I would put under "extras", so maybe
the title could be more specific since there will be more fluent
bindings in the future?

On Tue, Oct 5, 2021 at 1:34 PM John Hendrikx mailto:hj...@xs4all.nl>> wrote:

I've created https://bugs.openjdk.java.net/browse/JDK-8274771

Please have a look.

--John

On 04/10/2021 17:51, Nir Lisker wrote:
> I think that a PR can be created. The only point we need to decide
is about
> the subscription models we talked about above. ReactFX uses something
> different for each, you use the same. That can determine if we need
> different classes for each binding type.
>
> I can create the JBS issue for this one and a draft CSR if you want.
>
> On Tue, Sep 21, 2021 at 1:36 PM Nir Lisker mailto:nlis...@gmail.com>> wrote:
>
>> The OrElseBinding is incorrect
>>>
>>
>> Yes, that was a typo with the order of the arguments in the ternary
>> statement.
>>
>> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
>>> think the tests would become pretty unreadable and less useful /
>>> thourough otherwise).
>>>
>>> What are the options?
>>>
>>
>> This is a bigger question. Kevin will have to answer that.
>>
>> As for the subscription model: flat map has a more complicated
one, but
>>> orElse, orElseGet and map all have the same one.
>>>
>>
>> From what I saw, ReactFX uses a different subscription model for
these. I
>> could have misread it.
>>
>> The messages will need to be written from the perspective of the
Binding
>>> class then IMHO.
>>>
>>
>> That's fine.
>>
>> As for the Optional methods, I'll have a look in my code base and
see if
>> the places I would like to use them at will become irrelevant
anyway with
>> the fluent bindings. I'm fine with proceeding with the current
names of the
>> proposed methods.
>>
>> On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx mailto:hj...@xs4all.nl>> wrote:
>>
>>> I need to get you the tests I wrote, unfortunately, they're JUnit 5,
>>> please see the tests here:
>>>
>>>
>>>

https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
>>>
>>> The OrElseBinding is incorrect, the compute value should read:
>>>
>>>  protected T computeValue() {
>>>T value = source.getValue();
>>>return value == null ? constant : value;
>>>  }
>>>
>>> Similar for OrElseGetBinding.
>>>
>>> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
>>> think the tests would become pretty unreadable and less useful /
>>> thourough otherwise).
>>>
>>> What are the options?
>>>
>>> - Integrate a nested runner (there is an Apache 2.0 licensed one
>>> available)
>>> - Create our own nested runner (about 200 lines of code)
>>> - Can we introduce JUnit 5?
>>> - Rewrite to plain JUnit 4?
>>>
>>> Let me know, and I can integrate them.
>>>
>>> --John
>>>
>>> On 19/09/2021 02:12, Nir Lisker wrote:
 I've played around with the implementation and pushed a modified
 prototype to the sandbox [1]. I ended up with something similar to
>>> ReactFX:
 the orElse and orElseGet methods have their own binding classes
that
 extend LazyObjectBinding, just like MapBinding and
FlatMapBinding. The
 reason being that both their compute value and their
subscription models
 are slightly different. While they can be matched to MapBinding
like you
 did, it entails a bit of a roundabout way in my opinion. Creating a
 supplier for the constant value of orElse somewhat defeats the
purpose I
 think.
>>>
>>> I have no strong opinion here, just wanted to keep the MR small. The
>>> supplier should be an inline candidate, but creating a separate
class is
>>> fine too.
>>>
>>> As for the subscription model: flat map has a more complicated
one, but
>>> orElse, orElseGet and map all have the same one.
>>>
 In addition, I think that it's fine to move the arguments' null
checks
>>> to
 the binding classes themselves, even if it's a couple of levels
deeper
>>> on
 the stack, while adding a message in the 2nd argument of
 Objects.requireNonNull for clarity. These classes are
"self-contained"
>>> so
 it makes sense for them to check their arguments. They might be
used in
 other places, perhaps in the public Bindings class.
>>>
>>> The messages will need to be written from the perspective of the
B

Re: Proof of concept for fluent bindings for ObservableValue

2021-10-05 Thread Nir Lisker
Looks good. I assume we will add more bindings in the future like
conditionOn or anything else that I would put under "extras", so maybe the
title could be more specific since there will be more fluent bindings in
the future?

On Tue, Oct 5, 2021 at 1:34 PM John Hendrikx  wrote:

> I've created https://bugs.openjdk.java.net/browse/JDK-8274771
>
> Please have a look.
>
> --John
>
> On 04/10/2021 17:51, Nir Lisker wrote:
> > I think that a PR can be created. The only point we need to decide is
> about
> > the subscription models we talked about above. ReactFX uses something
> > different for each, you use the same. That can determine if we need
> > different classes for each binding type.
> >
> > I can create the JBS issue for this one and a draft CSR if you want.
> >
> > On Tue, Sep 21, 2021 at 1:36 PM Nir Lisker  wrote:
> >
> >> The OrElseBinding is incorrect
> >>>
> >>
> >> Yes, that was a typo with the order of the arguments in the ternary
> >> statement.
> >>
> >> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> >>> think the tests would become pretty unreadable and less useful /
> >>> thourough otherwise).
> >>>
> >>> What are the options?
> >>>
> >>
> >> This is a bigger question. Kevin will have to answer that.
> >>
> >> As for the subscription model: flat map has a more complicated one, but
> >>> orElse, orElseGet and map all have the same one.
> >>>
> >>
> >> From what I saw, ReactFX uses a different subscription model for these.
> I
> >> could have misread it.
> >>
> >> The messages will need to be written from the perspective of the Binding
> >>> class then IMHO.
> >>>
> >>
> >> That's fine.
> >>
> >> As for the Optional methods, I'll have a look in my code base and see if
> >> the places I would like to use them at will become irrelevant anyway
> with
> >> the fluent bindings. I'm fine with proceeding with the current names of
> the
> >> proposed methods.
> >>
> >> On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx  wrote:
> >>
> >>> I need to get you the tests I wrote, unfortunately, they're JUnit 5,
> >>> please see the tests here:
> >>>
> >>>
> >>>
> https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
> >>>
> >>> The OrElseBinding is incorrect, the compute value should read:
> >>>
> >>>  protected T computeValue() {
> >>>T value = source.getValue();
> >>>return value == null ? constant : value;
> >>>  }
> >>>
> >>> Similar for OrElseGetBinding.
> >>>
> >>> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> >>> think the tests would become pretty unreadable and less useful /
> >>> thourough otherwise).
> >>>
> >>> What are the options?
> >>>
> >>> - Integrate a nested runner (there is an Apache 2.0 licensed one
> >>> available)
> >>> - Create our own nested runner (about 200 lines of code)
> >>> - Can we introduce JUnit 5?
> >>> - Rewrite to plain JUnit 4?
> >>>
> >>> Let me know, and I can integrate them.
> >>>
> >>> --John
> >>>
> >>> On 19/09/2021 02:12, Nir Lisker wrote:
>  I've played around with the implementation and pushed a modified
>  prototype to the sandbox [1]. I ended up with something similar to
> >>> ReactFX:
>  the orElse and orElseGet methods have their own binding classes that
>  extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
>  reason being that both their compute value and their subscription
> models
>  are slightly different. While they can be matched to MapBinding like
> you
>  did, it entails a bit of a roundabout way in my opinion. Creating a
>  supplier for the constant value of orElse somewhat defeats the
> purpose I
>  think.
> >>>
> >>> I have no strong opinion here, just wanted to keep the MR small. The
> >>> supplier should be an inline candidate, but creating a separate class
> is
> >>> fine too.
> >>>
> >>> As for the subscription model: flat map has a more complicated one, but
> >>> orElse, orElseGet and map all have the same one.
> >>>
>  In addition, I think that it's fine to move the arguments' null checks
> >>> to
>  the binding classes themselves, even if it's a couple of levels deeper
> >>> on
>  the stack, while adding a message in the 2nd argument of
>  Objects.requireNonNull for clarity. These classes are "self-contained"
> >>> so
>  it makes sense for them to check their arguments. They might be used
> in
>  other places, perhaps in the public Bindings class.
> >>>
> >>> The messages will need to be written from the perspective of the
> Binding
> >>> class then IMHO.
> >>>
>  I also moved the subscription-related methods from ObservableValue to
>  static methods in Subscription, at least for now, to defer the API
> >>> related
>  to Subscription.
> >>>
> >>> Sounds good.
> >>>
>  The branch doesn't compile because I didn't finish working on the
>  visibility issue, but it's close enough to what I envision it like for
> >>> the
>  first PR

Re: Proof of concept for fluent bindings for ObservableValue

2021-10-05 Thread John Hendrikx

I've created https://bugs.openjdk.java.net/browse/JDK-8274771

Please have a look.

--John

On 04/10/2021 17:51, Nir Lisker wrote:

I think that a PR can be created. The only point we need to decide is about
the subscription models we talked about above. ReactFX uses something
different for each, you use the same. That can determine if we need
different classes for each binding type.

I can create the JBS issue for this one and a draft CSR if you want.

On Tue, Sep 21, 2021 at 1:36 PM Nir Lisker  wrote:


The OrElseBinding is incorrect




Yes, that was a typo with the order of the arguments in the ternary
statement.

I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I

think the tests would become pretty unreadable and less useful /
thourough otherwise).

What are the options?



This is a bigger question. Kevin will have to answer that.

As for the subscription model: flat map has a more complicated one, but

orElse, orElseGet and map all have the same one.



From what I saw, ReactFX uses a different subscription model for these. I
could have misread it.

The messages will need to be written from the perspective of the Binding

class then IMHO.



That's fine.

As for the Optional methods, I'll have a look in my code base and see if
the places I would like to use them at will become irrelevant anyway with
the fluent bindings. I'm fine with proceeding with the current names of the
proposed methods.

On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx  wrote:


I need to get you the tests I wrote, unfortunately, they're JUnit 5,
please see the tests here:


https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value

The OrElseBinding is incorrect, the compute value should read:

 protected T computeValue() {
   T value = source.getValue();
   return value == null ? constant : value;
 }

Similar for OrElseGetBinding.

I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
think the tests would become pretty unreadable and less useful /
thourough otherwise).

What are the options?

- Integrate a nested runner (there is an Apache 2.0 licensed one
available)
- Create our own nested runner (about 200 lines of code)
- Can we introduce JUnit 5?
- Rewrite to plain JUnit 4?

Let me know, and I can integrate them.

--John

On 19/09/2021 02:12, Nir Lisker wrote:

I've played around with the implementation and pushed a modified
prototype to the sandbox [1]. I ended up with something similar to

ReactFX:

the orElse and orElseGet methods have their own binding classes that
extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
reason being that both their compute value and their subscription models
are slightly different. While they can be matched to MapBinding like you
did, it entails a bit of a roundabout way in my opinion. Creating a
supplier for the constant value of orElse somewhat defeats the purpose I
think.


I have no strong opinion here, just wanted to keep the MR small. The
supplier should be an inline candidate, but creating a separate class is
fine too.

As for the subscription model: flat map has a more complicated one, but
orElse, orElseGet and map all have the same one.


In addition, I think that it's fine to move the arguments' null checks

to

the binding classes themselves, even if it's a couple of levels deeper

on

the stack, while adding a message in the 2nd argument of
Objects.requireNonNull for clarity. These classes are "self-contained"

so

it makes sense for them to check their arguments. They might be used in
other places, perhaps in the public Bindings class.


The messages will need to be written from the perspective of the Binding
class then IMHO.


I also moved the subscription-related methods from ObservableValue to
static methods in Subscription, at least for now, to defer the API

related

to Subscription.


Sounds good.


The branch doesn't compile because I didn't finish working on the
visibility issue, but it's close enough to what I envision it like for

the

first PR.


I've ported the changes over to my branch and ran the tests on it, they
all pass apart from the above mentioned problem in the OrElse bindings.


As for the java,util.Optional methods, I indeed did something like
`x.orElse(5).getValue()` in the past in other cases, but this approach
creates a new binding just to get the wrapped value out, which is very
inefficient. (In one case I did booleanValue.isNull().get(), which

creates

a boolean binding, because isPresent() does not exist). I would like to

see

what others think about the Optional methods, The binding method

variants

are much more important, but I want to avoid a name clash if the

Optional

ones will have support.


Okay, some more things to consider:

ObservableValue is not an Optional, their get methods respond
differently with Optional#get throwing an exception when null -- the
#orElse is a necessity; this is not the case for ObservableValue#getValue

Re: Proof of concept for fluent bindings for ObservableValue

2021-10-04 Thread John Hendrikx




On 04/10/2021 20:37, Michael Strauß wrote:

A little bit of bikeshedding here: I think this feature should be
thought of as a type-safe version of `Bindings.select`, which should
also be reflected in the terminology used (i.e. not map/flatMap). The
terminology, combined with the frequent comparisons to reactive
libraries might make it seem like an attempt to bring reactive
programming to JavaFX. That will certainly not happen, as JavaFX is
not (and never will be) a reactive framework. Naming it `select` will
convey the purpose and its place within JavaFX in a better and clearer
way.


The PoC is much more than that though. It allows arbitrary conversion of 
property values and creating bindings for these that are automatically 
kept updated. It not only removes the need for Bindings#select but also 
sort of obsoletes many of the functions offered by 
Object/String/IntegerExpression as for most of these #map can be used.


The terms #map and #flatMap (flatten + map) come from functional 
programming which predates reactive programming, and do not necessarily 
imply a stream as used by reactive. Java 8's Optional is a good example.


Both Optional and Stream are part of Java 8 and I contend are far better 
known within the Java community than reactive programming. Therefore the 
terms map and flatMap should be quite well understood by now.


--John


On Wed, Mar 24, 2021 at 10:49 PM John Hendrikx  wrote:


I just wanted to draw some attention to a recent proof of concept I made
in this pull request: https://github.com/openjdk/jfx/pull/434

It is based on the work I did in
https://github.com/hjohn/hs.jfx.eventstream which is in part based on
work done in ReactFX by Tomas Mikula. The PR itself however shares no
code with ReactFX and is
completely written by me.

If there is interest, I'm willing to invest more time in smoothing out
the API and documentation, investigating further how this would interact
with the primitive types and adding unit test coverage (I have extensive
tests, but thesea are written in JUnit 5, so they would require
conversion or JavaFX could move to support JUnit 5).

What follows below is the text of the PR for easy reading. Feedback is
appreciated.



This is a proof of concept of how fluent bindings could be introduced to
JavaFX. The main benefit of fluent bindings are ease of use, type safety
and less surprises. Features:

Flexible Mappings
Map the contents of a property any way you like with map, or map nested
properties with flatMap.

Lazy
The bindings created are lazy, which means they are always invalid when
not themselves observed. This allows for easier garbage collection (once
the last observer is removed, a chain of bindings will stop observing
their parents) and less listener management when dealing with nested
properties. Furthermore, this allows inclusion of such bindings in
classes such as Node without listeners being created when the binding
itself is not used (this would allow for the inclusion of a
treeShowingProperty in Node without creating excessive listeners, see
this fix I did in an earlier PR: #185)

Null Safe
The map and flatMap methods are skipped, similar to java.util.Optional
when the value they would be mapping is null. This makes mapping nested
properties with flatMap trivial as the null case does not need to be
taken into account in a chain like this:
node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty).
Instead a default can be provided with orElse or orElseGet.

Conditional Bindings
Bindings can be made conditional using the conditionOn method. A
conditional binding retains its last value when its condition is false.
Conditional bindings donot observe their source when the condition is
false, allowing developers to automatically stop listening to properties
when a certain condition is met. A major use of this feature is to have
UI components that need to keep models updated which may outlive the UI
conditionally update the long lived model only when the UI is showing.

Some examples:

void mapProperty() {
   // Standard JavaFX:
   label.textProperty().bind(Bindings.createStringBinding(() ->
text.getValueSafe().toUpperCase(), text));

   // Fluent: much more compact, no need to handle null
   label.textProperty().bind(text.map(String::toUpperCase));
}

void calculateCharactersLeft() {
   // Standard JavaFX:

label.textProperty().bind(text.length().negate().add(100).asString().concat("
characters left"));

   // Fluent: slightly more compact and more clear (no negate needed)
   label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() +
" characters left"));
}

void mapNestedValue() {
   // Standard JavaFX:
   label.textProperty().bind(Bindings.createStringBinding(
 () -> employee.get() == null ? ""
 : employee.get().getCompany() == null ? ""
 : employee.get().getCompany().getName(),
 employee
   ));

   // Fluent: no need to handle nulls everywhere
   label.textProperty(

Re: Proof of concept for fluent bindings for ObservableValue

2021-10-04 Thread John Hendrikx




On 04/10/2021 17:51, Nir Lisker wrote:

I think that a PR can be created. The only point we need to decide is
about the subscription models we talked about above. ReactFX uses
something different for each, you use the same. That can determine if we
need different classes for each binding type.


We can introduce classes for these in any case if that keeps things more 
clean; they're not public (after your change) so we can change our minds 
later.


I'm not sure I see the difference though, ReactFX does the same kind of 
observations to keep the bindings up to date. Here's a comparison:


OrElseConst (ReactFX) vs OrElseBinding
--

protected Subscription connect() {
return Val.observeInvalidations(src, obs -> invalidate());
}

--

protected Subscription observeInputs() {
return Subscription.subscribeInvalidations(source, 
this::invalidate); // start observing source

}

OrElseGetBinding (no equivalent in ReactFX)
---

@Override
protected Subscription observeInputs() {
return Subscription.subscribeInvalidations(source, 
this::invalidate); // start observing source

}

FlatMapped (ReactFX) vs FlatMapBinding
--

protected final Subscription connect() {
return Val.observeInvalidations(src, obs -> srcInvalidated())
.and(this::stopObservingSelected);
}

private void stopObservingSelected() {
if(selectedSubscription != null) {
selectedSubscription.unsubscribe();
selectedSubscription = null;
}
}

--

protected Subscription observeInputs() {
Subscription subscription = 
source.subscribeInvalidations(this::invalidate);


return () -> {
subscription.unsubscribe();
mappedSubscription.unsubscribe();
mappedSubscription = Subscription.EMPTY;
};
}

In both ReactFX and the PoC we see that FlatMapping requires a more 
complicated subscription. As a FlatMap observes the original observable 
and the result of the flat mapping, when unsubscribing two listeners 
need to be unsubscribed.


As for the OrElse and OrElseGet, I just noticed ReactFX does not have an 
#orElseGet equivalent. Closest thing is #getOrSupply but that doesn't 
create a binding and is just a convenience method to get the current 
value.


This does make me wonder how useful #orElseGet really would be -- I 
think we should leave it out for now, it is easy to introduce later if 
there is a real use case for it (I checked what I code I have, and I 
never used it so far, it was only used to delegate #orElse to #orElseGet 
which is pretty useless).


I think one more thing to decide is the naming of flatMap, there seems 
to be some support for using "select" as a name instead. I personally 
don't mind the name as for me flatMap just implies a map with the 
additional step of flattening the result (stripping the extra wrapper, 
be it Optional, Stream or in our case, ObservableValue).


I don't think that confusion with reactive programming is good argument 
to not use this name, as neither Optional nor Stream have resulted in 
such confusion.



I can create the JBS issue for this one and a draft CSR if you want.


It would be great to move this forward.  If we can't borrow one of the 
existing JBS issues then it would be good to create a new one, I can do 
that, I can rewrite the text of the PoC MR a bit for this purpose. As 
for a CSR, I'm not quite sure what that entails so it might be best if 
you draft one.


The draft PR is mostly missing the JUnit test coverage now, which I can 
take care off. I can also take care of the other changes we discussed 
that may not be there yet. And I'll take another critical look at the 
documentation and see if it still matches with what was discussed.


I've tried pushing the JUnit 5 upgrade as well, but I don't think it 
will arrive in time (or perhaps it will).  What I can do is convert my 
JUnit 5 tests to JUnit 4, and once JUnit 5 is available I can revert that.


--John


Re: Proof of concept for fluent bindings for ObservableValue

2021-10-04 Thread Michael Strauß
A little bit of bikeshedding here: I think this feature should be
thought of as a type-safe version of `Bindings.select`, which should
also be reflected in the terminology used (i.e. not map/flatMap). The
terminology, combined with the frequent comparisons to reactive
libraries might make it seem like an attempt to bring reactive
programming to JavaFX. That will certainly not happen, as JavaFX is
not (and never will be) a reactive framework. Naming it `select` will
convey the purpose and its place within JavaFX in a better and clearer
way.

On Wed, Mar 24, 2021 at 10:49 PM John Hendrikx  wrote:
>
> I just wanted to draw some attention to a recent proof of concept I made
> in this pull request: https://github.com/openjdk/jfx/pull/434
>
> It is based on the work I did in
> https://github.com/hjohn/hs.jfx.eventstream which is in part based on
> work done in ReactFX by Tomas Mikula. The PR itself however shares no
> code with ReactFX and is
> completely written by me.
>
> If there is interest, I'm willing to invest more time in smoothing out
> the API and documentation, investigating further how this would interact
> with the primitive types and adding unit test coverage (I have extensive
> tests, but thesea are written in JUnit 5, so they would require
> conversion or JavaFX could move to support JUnit 5).
>
> What follows below is the text of the PR for easy reading. Feedback is
> appreciated.
>
> 
>
> This is a proof of concept of how fluent bindings could be introduced to
> JavaFX. The main benefit of fluent bindings are ease of use, type safety
> and less surprises. Features:
>
> Flexible Mappings
> Map the contents of a property any way you like with map, or map nested
> properties with flatMap.
>
> Lazy
> The bindings created are lazy, which means they are always invalid when
> not themselves observed. This allows for easier garbage collection (once
> the last observer is removed, a chain of bindings will stop observing
> their parents) and less listener management when dealing with nested
> properties. Furthermore, this allows inclusion of such bindings in
> classes such as Node without listeners being created when the binding
> itself is not used (this would allow for the inclusion of a
> treeShowingProperty in Node without creating excessive listeners, see
> this fix I did in an earlier PR: #185)
>
> Null Safe
> The map and flatMap methods are skipped, similar to java.util.Optional
> when the value they would be mapping is null. This makes mapping nested
> properties with flatMap trivial as the null case does not need to be
> taken into account in a chain like this:
> node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty).
> Instead a default can be provided with orElse or orElseGet.
>
> Conditional Bindings
> Bindings can be made conditional using the conditionOn method. A
> conditional binding retains its last value when its condition is false.
> Conditional bindings donot observe their source when the condition is
> false, allowing developers to automatically stop listening to properties
> when a certain condition is met. A major use of this feature is to have
> UI components that need to keep models updated which may outlive the UI
> conditionally update the long lived model only when the UI is showing.
>
> Some examples:
>
> void mapProperty() {
>// Standard JavaFX:
>label.textProperty().bind(Bindings.createStringBinding(() ->
> text.getValueSafe().toUpperCase(), text));
>
>// Fluent: much more compact, no need to handle null
>label.textProperty().bind(text.map(String::toUpperCase));
> }
>
> void calculateCharactersLeft() {
>// Standard JavaFX:
>
> label.textProperty().bind(text.length().negate().add(100).asString().concat("
> characters left"));
>
>// Fluent: slightly more compact and more clear (no negate needed)
>label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() +
> " characters left"));
> }
>
> void mapNestedValue() {
>// Standard JavaFX:
>label.textProperty().bind(Bindings.createStringBinding(
>  () -> employee.get() == null ? ""
>  : employee.get().getCompany() == null ? ""
>  : employee.get().getCompany().getName(),
>  employee
>));
>
>// Fluent: no need to handle nulls everywhere
>label.textProperty().bind(
>  employee.map(Employee::getCompany)
>  .map(Company::getName)
>  .orElse("")
>);
> }
>
> void mapNestedProperty() {
>// Standard JavaFX:
>label.textProperty().bind(
>  Bindings.when(Bindings.selectBoolean(label.sceneProperty(),
> "window", "showing"))
>.then("Visible")
>.otherwise("Not Visible")
>);
>
>// Fluent: type safe
>label.textProperty().bind(label.sceneProperty()
>  .flatMap(Scene::windowProperty)
>  .flatMap(Window::showingProperty)
>  .orElse(false)
>  .map(showing -> showing ? "Visible" : "Not Visible")
>);
> }
>
> void updateLongLivedModelWhileAvo

Re: Proof of concept for fluent bindings for ObservableValue

2021-10-04 Thread Nir Lisker
I think that a PR can be created. The only point we need to decide is about
the subscription models we talked about above. ReactFX uses something
different for each, you use the same. That can determine if we need
different classes for each binding type.

I can create the JBS issue for this one and a draft CSR if you want.

On Tue, Sep 21, 2021 at 1:36 PM Nir Lisker  wrote:

> The OrElseBinding is incorrect
>>
>
> Yes, that was a typo with the order of the arguments in the ternary
> statement.
>
> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
>> think the tests would become pretty unreadable and less useful /
>> thourough otherwise).
>>
>> What are the options?
>>
>
> This is a bigger question. Kevin will have to answer that.
>
> As for the subscription model: flat map has a more complicated one, but
>> orElse, orElseGet and map all have the same one.
>>
>
> From what I saw, ReactFX uses a different subscription model for these. I
> could have misread it.
>
> The messages will need to be written from the perspective of the Binding
>> class then IMHO.
>>
>
> That's fine.
>
> As for the Optional methods, I'll have a look in my code base and see if
> the places I would like to use them at will become irrelevant anyway with
> the fluent bindings. I'm fine with proceeding with the current names of the
> proposed methods.
>
> On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx  wrote:
>
>> I need to get you the tests I wrote, unfortunately, they're JUnit 5,
>> please see the tests here:
>>
>>
>> https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
>>
>> The OrElseBinding is incorrect, the compute value should read:
>>
>>  protected T computeValue() {
>>T value = source.getValue();
>>return value == null ? constant : value;
>>  }
>>
>> Similar for OrElseGetBinding.
>>
>> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
>> think the tests would become pretty unreadable and less useful /
>> thourough otherwise).
>>
>> What are the options?
>>
>> - Integrate a nested runner (there is an Apache 2.0 licensed one
>> available)
>> - Create our own nested runner (about 200 lines of code)
>> - Can we introduce JUnit 5?
>> - Rewrite to plain JUnit 4?
>>
>> Let me know, and I can integrate them.
>>
>> --John
>>
>> On 19/09/2021 02:12, Nir Lisker wrote:
>> > I've played around with the implementation and pushed a modified
>> > prototype to the sandbox [1]. I ended up with something similar to
>> ReactFX:
>> > the orElse and orElseGet methods have their own binding classes that
>> > extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
>> > reason being that both their compute value and their subscription models
>> > are slightly different. While they can be matched to MapBinding like you
>> > did, it entails a bit of a roundabout way in my opinion. Creating a
>> > supplier for the constant value of orElse somewhat defeats the purpose I
>> > think.
>>
>> I have no strong opinion here, just wanted to keep the MR small. The
>> supplier should be an inline candidate, but creating a separate class is
>> fine too.
>>
>> As for the subscription model: flat map has a more complicated one, but
>> orElse, orElseGet and map all have the same one.
>>
>> > In addition, I think that it's fine to move the arguments' null checks
>> to
>> > the binding classes themselves, even if it's a couple of levels deeper
>> on
>> > the stack, while adding a message in the 2nd argument of
>> > Objects.requireNonNull for clarity. These classes are "self-contained"
>> so
>> > it makes sense for them to check their arguments. They might be used in
>> > other places, perhaps in the public Bindings class.
>>
>> The messages will need to be written from the perspective of the Binding
>> class then IMHO.
>>
>> > I also moved the subscription-related methods from ObservableValue to
>> > static methods in Subscription, at least for now, to defer the API
>> related
>> > to Subscription.
>>
>> Sounds good.
>>
>> > The branch doesn't compile because I didn't finish working on the
>> > visibility issue, but it's close enough to what I envision it like for
>> the
>> > first PR.
>>
>> I've ported the changes over to my branch and ran the tests on it, they
>> all pass apart from the above mentioned problem in the OrElse bindings.
>>
>> > As for the java,util.Optional methods, I indeed did something like
>> > `x.orElse(5).getValue()` in the past in other cases, but this approach
>> > creates a new binding just to get the wrapped value out, which is very
>> > inefficient. (In one case I did booleanValue.isNull().get(), which
>> creates
>> > a boolean binding, because isPresent() does not exist). I would like to
>> see
>> > what others think about the Optional methods, The binding method
>> variants
>> > are much more important, but I want to avoid a name clash if the
>> Optional
>> > ones will have support.
>>
>> Okay, some more things to consider:

Re: Re: Proof of concept for fluent bindings for ObservableValue

2021-09-26 Thread Eric Bresie
I’m no expert here so take it with a grain of salt  but…

I was proposing moving this sorts of ObservableValue type interfaces  and
implementations out of jfx (and React) context and into JDK context. Then
these sorts of observable type properties/ bindings can be used outside of
just UI contexts.

Eric

On Tue, Sep 21, 2021 at 12:27 PM Nir Lisker  wrote:

> I'm not sure what you're proposing. Move what, to where, and for what use?
>
> On Tue, Sep 21, 2021 at 8:02 PM Eric Bresie  wrote:
>
>> I’m very much an Observer here ;-) but
>>
>> Given the comparisons with FX and React implementations, is there value
>> in moving some of this out of here and into the JDK proper context making
>> it potentially usable outside of fx or react circles?
>>
>> I’m reminded of old JDK workings when someone might be working a headless
>> application needing a Swing dependency.
>>
>> Eric Bresie
>> ebre...@gmail.com (mailto:ebre...@gmail.com)
>
>
>>
>> > On September 21, 2021 at 5:36:13 AM CDT, Nir Lisker > (mailto:nlis...@gmail.com)> wrote:
>> > >
>> > > The OrElseBinding is incorrect
>> > >
>> >
>> > Yes, that was a typo with the order of the arguments in the ternary
>> > statement.
>> >
>> > I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
>> > > think the tests would become pretty unreadable and less useful /
>> > > thourough otherwise).
>> > >
>> > > What are the options?
>> > >
>> >
>> > This is a bigger question. Kevin will have to answer that.
>> >
>> > As for the subscription model: flat map has a more complicated one, but
>> > > orElse, orElseGet and map all have the same one.
>> > >
>> >
>> > From what I saw, ReactFX uses a different subscription model for these.
>> I
>> > could have misread it.
>> >
>> > The messages will need to be written from the perspective of the Binding
>> > > class then IMHO.
>> > >
>> >
>> > That's fine.
>> >
>> > As for the Optional methods, I'll have a look in my code base and see if
>> > the places I would like to use them at will become irrelevant anyway
>> with
>> > the fluent bindings. I'm fine with proceeding with the current names of
>> the
>> > proposed methods.
>> >
>> > On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx > hj...@xs4all.nl)> wrote:
>> >
>> > > I need to get you the tests I wrote, unfortunately, they're JUnit 5,
>> > > please see the tests here:
>> > >
>> > >
>> > >
>> https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
>> > >
>> > > The OrElseBinding is incorrect, the compute value should read:
>> > >
>> > > protected T computeValue() {
>> > > T value = source.getValue();
>> > > return value == null ? constant : value;
>> > > }
>> > >
>> > > Similar for OrElseGetBinding.
>> > >
>> > > I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
>> > > think the tests would become pretty unreadable and less useful /
>> > > thourough otherwise).
>> > >
>> > > What are the options?
>> > >
>> > > - Integrate a nested runner (there is an Apache 2.0 licensed one
>> available)
>> > > - Create our own nested runner (about 200 lines of code)
>> > > - Can we introduce JUnit 5?
>> > > - Rewrite to plain JUnit 4?
>> > >
>> > > Let me know, and I can integrate them.
>> > >
>> > > --John
>> > >
>> > > On 19/09/2021 02:12, Nir Lisker wrote:
>> > > > I've played around with the implementation and pushed a modified
>> > > > prototype to the sandbox [1]. I ended up with something similar to
>> > > ReactFX:
>> > > > the orElse and orElseGet methods have their own binding classes that
>> > > > extend LazyObjectBinding, just like MapBinding and FlatMapBinding.
>> The
>> > > > reason being that both their compute value and their subscription
>> models
>> > > > are slightly different. While they can be matched to MapBinding
>> like you
>> > > > did, it entails a bit of a roundabout way in my opinion. Creating a
>> > > > supplier for the constant value of orElse somewhat defeats the
>> purpose I
>> > > > think.
>> > >
>> > > I have no strong opinion here, just wanted to keep the MR small. The
>> > > supplier should be an inline candidate, but creating a separate class
>> is
>> > > fine too.
>> > >
>> > > As for the subscription model: flat map has a more complicated one,
>> but
>> > > orElse, orElseGet and map all have the same one.
>> > >
>> > > > In addition, I think that it's fine to move the arguments' null
>> checks to
>> > > > the binding classes themselves, even if it's a couple of levels
>> deeper on
>> > > > the stack, while adding a message in the 2nd argument of
>> > > > Objects.requireNonNull for clarity. These classes are
>> "self-contained" so
>> > > > it makes sense for them to check their arguments. They might be
>> used in
>> > > > other places, perhaps in the public Bindings class.
>> > >
>> > > The messages will need to be written from the perspective of the
>> Binding
>> > > class then IMHO.
>> > >
>> > > > I also moved the subscription-related methods from ObservableValue
>> to
>> > > > static meth

Re: Re: Proof of concept for fluent bindings for ObservableValue

2021-09-21 Thread Nir Lisker
I'm not sure what you're proposing. Move what, to where, and for what use?

On Tue, Sep 21, 2021 at 8:02 PM Eric Bresie  wrote:

> I’m very much an Observer here ;-) but
>
> Given the comparisons with FX and React implementations, is there value in
> moving some of this out of here and into the JDK proper context making it
> potentially usable outside of fx or react circles?
>
> I’m reminded of old JDK workings when someone might be working a headless
> application needing a Swing dependency.
>
> Eric Bresie
> ebre...@gmail.com (mailto:ebre...@gmail.com)
>
> > On September 21, 2021 at 5:36:13 AM CDT, Nir Lisker  (mailto:nlis...@gmail.com)> wrote:
> > >
> > > The OrElseBinding is incorrect
> > >
> >
> > Yes, that was a typo with the order of the arguments in the ternary
> > statement.
> >
> > I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> > > think the tests would become pretty unreadable and less useful /
> > > thourough otherwise).
> > >
> > > What are the options?
> > >
> >
> > This is a bigger question. Kevin will have to answer that.
> >
> > As for the subscription model: flat map has a more complicated one, but
> > > orElse, orElseGet and map all have the same one.
> > >
> >
> > From what I saw, ReactFX uses a different subscription model for these. I
> > could have misread it.
> >
> > The messages will need to be written from the perspective of the Binding
> > > class then IMHO.
> > >
> >
> > That's fine.
> >
> > As for the Optional methods, I'll have a look in my code base and see if
> > the places I would like to use them at will become irrelevant anyway with
> > the fluent bindings. I'm fine with proceeding with the current names of
> the
> > proposed methods.
> >
> > On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx  hj...@xs4all.nl)> wrote:
> >
> > > I need to get you the tests I wrote, unfortunately, they're JUnit 5,
> > > please see the tests here:
> > >
> > >
> > >
> https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
> > >
> > > The OrElseBinding is incorrect, the compute value should read:
> > >
> > > protected T computeValue() {
> > > T value = source.getValue();
> > > return value == null ? constant : value;
> > > }
> > >
> > > Similar for OrElseGetBinding.
> > >
> > > I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> > > think the tests would become pretty unreadable and less useful /
> > > thourough otherwise).
> > >
> > > What are the options?
> > >
> > > - Integrate a nested runner (there is an Apache 2.0 licensed one
> available)
> > > - Create our own nested runner (about 200 lines of code)
> > > - Can we introduce JUnit 5?
> > > - Rewrite to plain JUnit 4?
> > >
> > > Let me know, and I can integrate them.
> > >
> > > --John
> > >
> > > On 19/09/2021 02:12, Nir Lisker wrote:
> > > > I've played around with the implementation and pushed a modified
> > > > prototype to the sandbox [1]. I ended up with something similar to
> > > ReactFX:
> > > > the orElse and orElseGet methods have their own binding classes that
> > > > extend LazyObjectBinding, just like MapBinding and FlatMapBinding.
> The
> > > > reason being that both their compute value and their subscription
> models
> > > > are slightly different. While they can be matched to MapBinding like
> you
> > > > did, it entails a bit of a roundabout way in my opinion. Creating a
> > > > supplier for the constant value of orElse somewhat defeats the
> purpose I
> > > > think.
> > >
> > > I have no strong opinion here, just wanted to keep the MR small. The
> > > supplier should be an inline candidate, but creating a separate class
> is
> > > fine too.
> > >
> > > As for the subscription model: flat map has a more complicated one, but
> > > orElse, orElseGet and map all have the same one.
> > >
> > > > In addition, I think that it's fine to move the arguments' null
> checks to
> > > > the binding classes themselves, even if it's a couple of levels
> deeper on
> > > > the stack, while adding a message in the 2nd argument of
> > > > Objects.requireNonNull for clarity. These classes are
> "self-contained" so
> > > > it makes sense for them to check their arguments. They might be used
> in
> > > > other places, perhaps in the public Bindings class.
> > >
> > > The messages will need to be written from the perspective of the
> Binding
> > > class then IMHO.
> > >
> > > > I also moved the subscription-related methods from ObservableValue to
> > > > static methods in Subscription, at least for now, to defer the API
> > > related
> > > > to Subscription.
> > >
> > > Sounds good.
> > >
> > > > The branch doesn't compile because I didn't finish working on the
> > > > visibility issue, but it's close enough to what I envision it like
> for
> > > the
> > > > first PR.
> > >
> > > I've ported the changes over to my branch and ran the tests on it, they
> > > all pass apart from the above mentioned problem in the OrElse bindings.
> > >
> > > > As for the java,util.

Re: Re: Proof of concept for fluent bindings for ObservableValue

2021-09-21 Thread Eric Bresie
I’m very much an Observer here ;-) but

Given the comparisons with FX and React implementations, is there value in 
moving some of this out of here and into the JDK proper context making it 
potentially usable outside of fx or react circles?

I’m reminded of old JDK workings when someone might be working a headless 
application needing a Swing dependency.

Eric Bresie
ebre...@gmail.com (mailto:ebre...@gmail.com)

> On September 21, 2021 at 5:36:13 AM CDT, Nir Lisker  (mailto:nlis...@gmail.com)> wrote:
> >
> > The OrElseBinding is incorrect
> >
>
> Yes, that was a typo with the order of the arguments in the ternary
> statement.
>
> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> > think the tests would become pretty unreadable and less useful /
> > thourough otherwise).
> >
> > What are the options?
> >
>
> This is a bigger question. Kevin will have to answer that.
>
> As for the subscription model: flat map has a more complicated one, but
> > orElse, orElseGet and map all have the same one.
> >
>
> From what I saw, ReactFX uses a different subscription model for these. I
> could have misread it.
>
> The messages will need to be written from the perspective of the Binding
> > class then IMHO.
> >
>
> That's fine.
>
> As for the Optional methods, I'll have a look in my code base and see if
> the places I would like to use them at will become irrelevant anyway with
> the fluent bindings. I'm fine with proceeding with the current names of the
> proposed methods.
>
> On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx  (mailto:hj...@xs4all.nl)> wrote:
>
> > I need to get you the tests I wrote, unfortunately, they're JUnit 5,
> > please see the tests here:
> >
> >
> > https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
> >
> > The OrElseBinding is incorrect, the compute value should read:
> >
> > protected T computeValue() {
> > T value = source.getValue();
> > return value == null ? constant : value;
> > }
> >
> > Similar for OrElseGetBinding.
> >
> > I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> > think the tests would become pretty unreadable and less useful /
> > thourough otherwise).
> >
> > What are the options?
> >
> > - Integrate a nested runner (there is an Apache 2.0 licensed one available)
> > - Create our own nested runner (about 200 lines of code)
> > - Can we introduce JUnit 5?
> > - Rewrite to plain JUnit 4?
> >
> > Let me know, and I can integrate them.
> >
> > --John
> >
> > On 19/09/2021 02:12, Nir Lisker wrote:
> > > I've played around with the implementation and pushed a modified
> > > prototype to the sandbox [1]. I ended up with something similar to
> > ReactFX:
> > > the orElse and orElseGet methods have their own binding classes that
> > > extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
> > > reason being that both their compute value and their subscription models
> > > are slightly different. While they can be matched to MapBinding like you
> > > did, it entails a bit of a roundabout way in my opinion. Creating a
> > > supplier for the constant value of orElse somewhat defeats the purpose I
> > > think.
> >
> > I have no strong opinion here, just wanted to keep the MR small. The
> > supplier should be an inline candidate, but creating a separate class is
> > fine too.
> >
> > As for the subscription model: flat map has a more complicated one, but
> > orElse, orElseGet and map all have the same one.
> >
> > > In addition, I think that it's fine to move the arguments' null checks to
> > > the binding classes themselves, even if it's a couple of levels deeper on
> > > the stack, while adding a message in the 2nd argument of
> > > Objects.requireNonNull for clarity. These classes are "self-contained" so
> > > it makes sense for them to check their arguments. They might be used in
> > > other places, perhaps in the public Bindings class.
> >
> > The messages will need to be written from the perspective of the Binding
> > class then IMHO.
> >
> > > I also moved the subscription-related methods from ObservableValue to
> > > static methods in Subscription, at least for now, to defer the API
> > related
> > > to Subscription.
> >
> > Sounds good.
> >
> > > The branch doesn't compile because I didn't finish working on the
> > > visibility issue, but it's close enough to what I envision it like for
> > the
> > > first PR.
> >
> > I've ported the changes over to my branch and ran the tests on it, they
> > all pass apart from the above mentioned problem in the OrElse bindings.
> >
> > > As for the java,util.Optional methods, I indeed did something like
> > > `x.orElse(5).getValue()` in the past in other cases, but this approach
> > > creates a new binding just to get the wrapped value out, which is very
> > > inefficient. (In one case I did booleanValue.isNull().get(), which
> > creates
> > > a boolean binding, because isPresent() does not exist). I would like to
> > see
> > > what others t

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-21 Thread Nir Lisker
>
> The OrElseBinding is incorrect
>

Yes, that was a typo with the order of the arguments in the ternary
statement.

I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> think the tests would become pretty unreadable and less useful /
> thourough otherwise).
>
> What are the options?
>

This is a bigger question. Kevin will have to answer that.

As for the subscription model: flat map has a more complicated one, but
> orElse, orElseGet and map all have the same one.
>

>From what I saw, ReactFX uses a different subscription model for these. I
could have misread it.

The messages will need to be written from the perspective of the Binding
> class then IMHO.
>

That's fine.

As for the Optional methods, I'll have a look in my code base and see if
the places I would like to use them at will become irrelevant anyway with
the fluent bindings. I'm fine with proceeding with the current names of the
proposed methods.

On Sun, Sep 19, 2021 at 6:12 PM John Hendrikx  wrote:

> I need to get you the tests I wrote, unfortunately, they're JUnit 5,
> please see the tests here:
>
>
> https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value
>
> The OrElseBinding is incorrect, the compute value should read:
>
>  protected T computeValue() {
>T value = source.getValue();
>return value == null ? constant : value;
>  }
>
> Similar for OrElseGetBinding.
>
> I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I
> think the tests would become pretty unreadable and less useful /
> thourough otherwise).
>
> What are the options?
>
> - Integrate a nested runner (there is an Apache 2.0 licensed one available)
> - Create our own nested runner (about 200 lines of code)
> - Can we introduce JUnit 5?
> - Rewrite to plain JUnit 4?
>
> Let me know, and I can integrate them.
>
> --John
>
> On 19/09/2021 02:12, Nir Lisker wrote:
> > I've played around with the implementation and pushed a modified
> > prototype to the sandbox [1]. I ended up with something similar to
> ReactFX:
> > the orElse and orElseGet methods have their own binding classes that
> > extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
> > reason being that both their compute value and their subscription models
> > are slightly different. While they can be matched to MapBinding like you
> > did, it entails a bit of a roundabout way in my opinion. Creating a
> > supplier for the constant value of orElse somewhat defeats the purpose I
> > think.
>
> I have no strong opinion here, just wanted to keep the MR small. The
> supplier should be an inline candidate, but creating a separate class is
> fine too.
>
> As for the subscription model: flat map has a more complicated one, but
> orElse, orElseGet and map all have the same one.
>
> > In addition, I think that it's fine to move the arguments' null checks to
> > the binding classes themselves, even if it's a couple of levels deeper on
> > the stack, while adding a message in the 2nd argument of
> > Objects.requireNonNull for clarity. These classes are "self-contained" so
> > it makes sense for them to check their arguments. They might be used in
> > other places, perhaps in the public Bindings class.
>
> The messages will need to be written from the perspective of the Binding
> class then IMHO.
>
> > I also moved the subscription-related methods from ObservableValue to
> > static methods in Subscription, at least for now, to defer the API
> related
> > to Subscription.
>
> Sounds good.
>
> > The branch doesn't compile because I didn't finish working on the
> > visibility issue, but it's close enough to what I envision it like for
> the
> > first PR.
>
> I've ported the changes over to my branch and ran the tests on it, they
> all pass apart from the above mentioned problem in the OrElse bindings.
>
> > As for the java,util.Optional methods, I indeed did something like
> > `x.orElse(5).getValue()` in the past in other cases, but this approach
> > creates a new binding just to get the wrapped value out, which is very
> > inefficient. (In one case I did booleanValue.isNull().get(), which
> creates
> > a boolean binding, because isPresent() does not exist). I would like to
> see
> > what others think about the Optional methods, The binding method variants
> > are much more important, but I want to avoid a name clash if the Optional
> > ones will have support.
>
> Okay, some more things to consider:
>
> ObservableValue is not an Optional, their get methods respond
> differently with Optional#get throwing an exception when null -- the
> #orElse is a necessity; this is not the case for ObservableValue#getValue.
>
> When creating mapping chains, you are going to do this because you want
> to bind this to another property or to listen on it (with subscribe).
> You don't want to do this as a one time thing. If you are creating a
> chain just to calculate a value you can just do this directly. Instead of:
>
>  widthPro

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-19 Thread John Hendrikx
I need to get you the tests I wrote, unfortunately, they're JUnit 5, 
please see the tests here:


https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx/src/test/java/javafx/beans/value

The OrElseBinding is incorrect, the compute value should read:

protected T computeValue() {
  T value = source.getValue();
  return value == null ? constant : value;
}

Similar for OrElseGetBinding.

I can rewrite the tests for JUnit 4 but I'd need a Nested runner (I 
think the tests would become pretty unreadable and less useful / 
thourough otherwise).


What are the options?

- Integrate a nested runner (there is an Apache 2.0 licensed one available)
- Create our own nested runner (about 200 lines of code)
- Can we introduce JUnit 5?
- Rewrite to plain JUnit 4?

Let me know, and I can integrate them.

--John

On 19/09/2021 02:12, Nir Lisker wrote:

I've played around with the implementation and pushed a modified
prototype to the sandbox [1]. I ended up with something similar to ReactFX:
the orElse and orElseGet methods have their own binding classes that
extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
reason being that both their compute value and their subscription models
are slightly different. While they can be matched to MapBinding like you
did, it entails a bit of a roundabout way in my opinion. Creating a
supplier for the constant value of orElse somewhat defeats the purpose I
think.


I have no strong opinion here, just wanted to keep the MR small. The 
supplier should be an inline candidate, but creating a separate class is 
fine too.


As for the subscription model: flat map has a more complicated one, but 
orElse, orElseGet and map all have the same one.



In addition, I think that it's fine to move the arguments' null checks to
the binding classes themselves, even if it's a couple of levels deeper on
the stack, while adding a message in the 2nd argument of
Objects.requireNonNull for clarity. These classes are "self-contained" so
it makes sense for them to check their arguments. They might be used in
other places, perhaps in the public Bindings class.


The messages will need to be written from the perspective of the Binding 
class then IMHO.



I also moved the subscription-related methods from ObservableValue to
static methods in Subscription, at least for now, to defer the API related
to Subscription.


Sounds good.


The branch doesn't compile because I didn't finish working on the
visibility issue, but it's close enough to what I envision it like for the
first PR.


I've ported the changes over to my branch and ran the tests on it, they 
all pass apart from the above mentioned problem in the OrElse bindings.



As for the java,util.Optional methods, I indeed did something like
`x.orElse(5).getValue()` in the past in other cases, but this approach
creates a new binding just to get the wrapped value out, which is very
inefficient. (In one case I did booleanValue.isNull().get(), which creates
a boolean binding, because isPresent() does not exist). I would like to see
what others think about the Optional methods, The binding method variants
are much more important, but I want to avoid a name clash if the Optional
ones will have support.


Okay, some more things to consider:

ObservableValue is not an Optional, their get methods respond 
differently with Optional#get throwing an exception when null -- the 
#orElse is a necessity; this is not the case for ObservableValue#getValue.


When creating mapping chains, you are going to do this because you want 
to bind this to another property or to listen on it (with subscribe). 
You don't want to do this as a one time thing. If you are creating a 
chain just to calculate a value you can just do this directly. Instead of:


widthProperty().map(x -> x * 2).getValue()

You'd do:

getWidth() * 2;

With flat mapping however this is not as easy:

[1]
node.sceneProperty()
  .flatMap(Scene::windowProperty)
  .flatMap(Window::showingProperty)
  .orElse(false);

You could try:

node.getScene().getWindow().isShowing();

But that will not work when Scene or Window is null.  You'd have to 
write it as:


[2]
Optional.ofNullable(node.getScene())
   .map(Scene::getWindow)
   .map(Window::isShowing)
   .orElse(false);

If you only offer a "specialized" orElse, you'd still need to create 
several bindings:


[3]
node.sceneProperty()
  .flatMap(Scene::windowProperty)
  .flatMap(Window::showingProperty)
  .orElse2(false);  // orElse which returns a value not a binding

If you compare [2] (which is possible now) with [3] the difference is 
minimal. A bit more ceremony at the start for [2] but a shorter, cleaner 
and faster mapping (no bindings and no need to use the property methods).


Now, if you already HAVE the binding for some other purpose, then it is 
highly likely it also already has an #orElse that is needed as part of 
the binding. In that case callin

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-18 Thread Nir Lisker
I've played around with the implementation and pushed a modified
prototype to the sandbox [1]. I ended up with something similar to ReactFX:
the orElse and orElseGet methods have their own binding classes that
extend LazyObjectBinding, just like MapBinding and FlatMapBinding. The
reason being that both their compute value and their subscription models
are slightly different. While they can be matched to MapBinding like you
did, it entails a bit of a roundabout way in my opinion. Creating a
supplier for the constant value of orElse somewhat defeats the purpose I
think.

In addition, I think that it's fine to move the arguments' null checks to
the binding classes themselves, even if it's a couple of levels deeper on
the stack, while adding a message in the 2nd argument of
Objects.requireNonNull for clarity. These classes are "self-contained" so
it makes sense for them to check their arguments. They might be used in
other places, perhaps in the public Bindings class.

I also moved the subscription-related methods from ObservableValue to
static methods in Subscription, at least for now, to defer the API related
to Subscription.

The branch doesn't compile because I didn't finish working on the
visibility issue, but it's close enough to what I envision it like for the
first PR.

As for the java,util.Optional methods, I indeed did something like
`x.orElse(5).getValue()` in the past in other cases, but this approach
creates a new binding just to get the wrapped value out, which is very
inefficient. (In one case I did booleanValue.isNull().get(), which creates
a boolean binding, because isPresent() does not exist). I would like to see
what others think about the Optional methods, The binding method variants
are much more important, but I want to avoid a name clash if the Optional
ones will have support.

[1]
https://github.com/openjdk/jfx-sandbox/tree/jfx-sandbox/nlisker_fuent_bindings

On Wed, Sep 15, 2021 at 1:59 PM John Hendrikx  wrote:

>
>
> On 15/09/2021 02:28, Nir Lisker wrote:
> > Sorry, I'm not quite sure what you mean by this. Could you elaborate?
> > The methods orElse and orElseGet are present in the PoC, and I think
> > they're highly useful to have to deal with nulls.
> >
> >
> > The methods that you call orElse and orElseGet return an
> > ObservableValue. The Optional methods with the same names return the
> > wrapped value (of type T). For comparison, ReactFX has:
> > T getOrElse(T defaultValue)
> > T getOrSupply(Supplier defaultSupplier)
> > Val orElseConst(T other)
> > Val orElse(ObservableValue other)
>
> I see what you mean now. The methods from java.util.Optional will return
> an unwrapped value, while the ones from ObservableValue in the PoC
> return an ObservableValue, but they have the same name.
>
> So java.util.Optional offers:
>
>  T orElse(T other)
>  T orElseGet(Supplier supplier)
>
> And the PoC:
>
>  ObservableValue orElse(T alternativeValue)
>  ObservableValue orElseGet(Supplier supplier)
>
> The main difference is in the returned types. Personally, I think it is
> rarely useful for a binding to be queried directly and I've never used
> the #getOrElse or #getOrSupply variants that ReactFX offers. On top of
> that:
>
>  x.orElse(5).getValue()===x.getOrElse(5)
>
> So it introduces another method in the interface to avoid having to
> write ".getValue()". The opposite, introducing only the "unwrapped"
> variants would still require the "wrapped" variants to be present as well.
>
> So, what I would suggest is to not add variants for #getOrElse and
> #getOrSupply at all as they are of questionable value and would just
> bloat the API for a bit less typing. In that case I think we can still
> use the signatures as they are.
>
> ReactFX also offers:
>
>  Val orElse(ObservableValue other)
>
> This is another rarely used feature IMHO, and I think Optional#or would
> a better match for this functionality:
>
>  Optional or(Supplier> supplier)
>
> In the POC the signature would be:
>
>  ObservableValue or(
>Supplier> supplier
>  )
>
> I didn't implement this one in the PoC to keep it small, but I did
> implement this in my JavaFX EventStream library before.
>
> > So it deals with both getting the wrapped value in null cases and with
> > getting a "dynamic value" in null cases. I find the Optional-like method
> > 'T getOrElse(T defaultValue)' useful (along with others such as
> > ifPresent because Optional is just useful for dealing with wrapped
> > values). What I'm saying is that we should be careful with the names if
> > we include both "constant" and "dynamic" versions of 'orElse'-like
> methods.
>
> I think #ifPresent can be added, not so sure about the usefulness of
> #getOrElse (see above).
>
> > The null check in ReactFX's #computeValue is
> > actually checking the result of the mapping function, not whether the
> > function instance itself was null.
> >
> > I didn't mean the null-ness of the map argument. What I 

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-15 Thread John Hendrikx




On 15/09/2021 02:28, Nir Lisker wrote:

Sorry, I'm not quite sure what you mean by this. Could you elaborate?
The methods orElse and orElseGet are present in the PoC, and I think
they're highly useful to have to deal with nulls.


The methods that you call orElse and orElseGet return an
ObservableValue. The Optional methods with the same names return the
wrapped value (of type T). For comparison, ReactFX has:
T getOrElse(T defaultValue)
T getOrSupply(Supplier defaultSupplier)
Val orElseConst(T other)
Val orElse(ObservableValue other)


I see what you mean now. The methods from java.util.Optional will return 
an unwrapped value, while the ones from ObservableValue in the PoC 
return an ObservableValue, but they have the same name.


So java.util.Optional offers:

T orElse(T other)
T orElseGet(Supplier supplier)

And the PoC:

ObservableValue orElse(T alternativeValue)
ObservableValue orElseGet(Supplier supplier)

The main difference is in the returned types. Personally, I think it is 
rarely useful for a binding to be queried directly and I've never used 
the #getOrElse or #getOrSupply variants that ReactFX offers. On top of that:


x.orElse(5).getValue()===x.getOrElse(5)

So it introduces another method in the interface to avoid having to 
write ".getValue()". The opposite, introducing only the "unwrapped" 
variants would still require the "wrapped" variants to be present as well.


So, what I would suggest is to not add variants for #getOrElse and 
#getOrSupply at all as they are of questionable value and would just 
bloat the API for a bit less typing. In that case I think we can still 
use the signatures as they are.


ReactFX also offers:

Val orElse(ObservableValue other)

This is another rarely used feature IMHO, and I think Optional#or would 
a better match for this functionality:


Optional or(Supplier> supplier)

In the POC the signature would be:

ObservableValue or(
  Supplier> supplier
)

I didn't implement this one in the PoC to keep it small, but I did 
implement this in my JavaFX EventStream library before.



So it deals with both getting the wrapped value in null cases and with
getting a "dynamic value" in null cases. I find the Optional-like method
'T getOrElse(T defaultValue)' useful (along with others such as
ifPresent because Optional is just useful for dealing with wrapped
values). What I'm saying is that we should be careful with the names if
we include both "constant" and "dynamic" versions of 'orElse'-like methods.


I think #ifPresent can be added, not so sure about the usefulness of 
#getOrElse (see above).



The null check in ReactFX's #computeValue is
actually checking the result of the mapping function, not whether the
function instance itself was null.

I didn't mean the null-ness of the map argument. What I meant was that
there is this implementation, which is similar to what ReactFX does:


Sorry, I see it now. You have a good point, the current implementation 
indeed wraps another Lambda to add the null check which could have been 
done in #computeValue. I think it would be a good move to avoid the 
extra lambda simply because the end result would be more readable -- any 
performance improvement would be a bonus, I don't know if there will be any.



As for my points 3 and 4, I'll have to try and play with the
implementation myself, which will be easier to do when there is some PR
in the works.


Perhaps this is useful: 
https://github.com/hjohn/MediaSystem-v2/tree/master/mediasystem-jfx


When you add this as a maven dependency to your project, you will get 
the new PoC functionality. It basically uses the Maven shade plugin to 
replace a few classes in javafx-base -- I use this sometimes to fix bugs 
I need fixed immediately by patching jfx, but found it also works very 
nicely to experiment with this PoC.


Also, the PoC PR compiles fine, it may need rebasing.


To close "Bindings.select*(): add new type-safe template based API
instead of legacy-style set of methods" we'd need the flatMap/select
method to be included.

Yes. I think that we can include flatMap in this iteration, perhaps as
a separate PR?


That should be no problem, I can split it up.


I don't think we really need specialized methods for primitives (or at
least, not right away).  At this point the primitive versions only
really differ in what value they'd return if the binding would be null
and perhaps they do a little less boxing/unboxing. Since you can select
the default value with #orElse which is more flexible. I don't see much
use to add those variants.

I agree, I would avoid bloating the primitive specialization classes for
now, especially when Valhalla is planned to take care of those.


Yes, I think we can easily do without for now.


The ticket is a bit unclear as I can't see what type "x" is.

Yes, but I got the impression that either way it can be solved with map
(or flatMap).

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-14 Thread Nir Lisker
>
> Sorry, I'm not quite sure what you mean by this. Could you elaborate?
> The methods orElse and orElseGet are present in the PoC, and I think
> they're highly useful to have to deal with nulls.


The methods that you call orElse and orElseGet return an
ObservableValue. The Optional methods with the same names return the
wrapped value (of type T). For comparison, ReactFX has:
T getOrElse(T defaultValue)
T getOrSupply(Supplier defaultSupplier)
Val orElseConst(T other)
Val orElse(ObservableValue other)

So it deals with both getting the wrapped value in null cases and with
getting a "dynamic value" in null cases. I find the Optional-like method 'T
getOrElse(T defaultValue)' useful (along with others such as ifPresent
because Optional is just useful for dealing with wrapped values). What I'm
saying is that we should be careful with the names if we include both
"constant" and "dynamic" versions of 'orElse'-like methods.

The null check in ReactFX's #computeValue is
> actually checking the result of the mapping function, not whether the
> function instance itself was null.
>

I didn't mean the null-ness of the map argument. What I meant was that
there is this implementation, which is similar to what ReactFX does:

  public static  ObservableValue mapping(ObservableValue
source, Function mapper) {
return nullableMapping(source, mapper.apply(v)); // don't deal with a
null v here
  }

  public static  ObservableValue
nullableMapping(ObservableValue source, Function
mapper) {
return new LazyObjectBinding<>() {
   ...

  @Override
  protected U computeValue() {
T value = source.getValue();
return value == null ? null ? mapper.apply(value); // do it here
instead
  }
};
  }

---

As for my points 3 and 4, I'll have to try and play with the implementation
myself, which will be easier to do when there is some PR in the works.

To close "Bindings.select*(): add new type-safe template based API
> instead of legacy-style set of methods" we'd need the flatMap/select
> method to be included.


 Yes. I think that we can include flatMap in this iteration, perhaps as a
separate PR?

I don't think we really need specialized methods for primitives (or at
> least, not right away).  At this point the primitive versions only
> really differ in what value they'd return if the binding would be null
> and perhaps they do a little less boxing/unboxing. Since you can select
> the default value with #orElse which is more flexible. I don't see much
> use to add those variants.


I agree, I would avoid bloating the primitive specialization classes for
now, especially when Valhalla is planned to take care of those.

The ticket is a bit unclear as I can't see what type "x" is.
>

Yes, but I got the impression that either way it can be solved with map (or
flatMap).

On Tue, Sep 14, 2021 at 8:44 AM John Hendrikx  wrote:

>
>
> On 14/09/2021 03:14, Nir Lisker wrote:
> > Sounds good.
> >
> > Some points I have (maybe some are premature):
> >
> > 1. I still think that adding the Optional methods for orElse and
> orElseGet
> > could be useful. Unless I can be convinced otherwise, I suggest that we
> be
> > careful with the naming of current methods that return a binding.
>
> Sorry, I'm not quite sure what you mean by this. Could you elaborate?
> The methods orElse and orElseGet are present in the PoC, and I think
> they're highly useful to have to deal with nulls.
>
> > 2. I see that in ReactFX the Val.map will pass to MappedVal the mapping
> > function as-is, and the null check is done in computeValue(). In your
> > implementation, the LazyBinding (equivalent of MappedVal) is passed a
> > composite mapping that deals with null and the computeValue() can just
> use
> > that new mapping function. I think that the end behavior is the same, but
> > does your way use more memory for the extra Function lambda?
>
> I think you may have misinterpreted what happens here. I'm ensuring that
> the mapping function itself isn't null (fail fast) by using
> Objects.requireNonNull -- it doesn't produce a new lambda. ReactFX
> doesn't do this check and will throw a NPE when the mapping function is
> used for the first time. The null check in ReactFX's #computeValue is
> actually checking the result of the mapping function, not whether the
> function instance itself was null.
>
> > 3. Why does nullableMapping creates an anonymous subtype of LazyBinding,
> > while flatMapping creates a concrete instance of FlatMapBinding?
>
> I suppose I felt it was small enough to inline, but there's no
> particular reason to write it one way or the other. The nullableMapping
> can be extracted to a class to make it more consistent.
>
> > 4. Why does orElseGet call Bindings.nullableMapping directly, while map
> > calls Bindings.mapping which in turn calls Bindings.nullableMapping?
>
> It's because of the null check. For mapping I could wrap the function
> parameter in Objects.requireNonNull as it will be evaluated immediately.
> For orE

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-13 Thread John Hendrikx




On 14/09/2021 03:14, Nir Lisker wrote:

Sounds good.

Some points I have (maybe some are premature):

1. I still think that adding the Optional methods for orElse and orElseGet
could be useful. Unless I can be convinced otherwise, I suggest that we be
careful with the naming of current methods that return a binding.


Sorry, I'm not quite sure what you mean by this. Could you elaborate? 
The methods orElse and orElseGet are present in the PoC, and I think 
they're highly useful to have to deal with nulls.



2. I see that in ReactFX the Val.map will pass to MappedVal the mapping
function as-is, and the null check is done in computeValue(). In your
implementation, the LazyBinding (equivalent of MappedVal) is passed a
composite mapping that deals with null and the computeValue() can just use
that new mapping function. I think that the end behavior is the same, but
does your way use more memory for the extra Function lambda?


I think you may have misinterpreted what happens here. I'm ensuring that 
the mapping function itself isn't null (fail fast) by using 
Objects.requireNonNull -- it doesn't produce a new lambda. ReactFX 
doesn't do this check and will throw a NPE when the mapping function is 
used for the first time. The null check in ReactFX's #computeValue is 
actually checking the result of the mapping function, not whether the 
function instance itself was null.



3. Why does nullableMapping creates an anonymous subtype of LazyBinding,
while flatMapping creates a concrete instance of FlatMapBinding?


I suppose I felt it was small enough to inline, but there's no 
particular reason to write it one way or the other. The nullableMapping 
can be extracted to a class to make it more consistent.



4. Why does orElseGet call Bindings.nullableMapping directly, while map
calls Bindings.mapping which in turn calls Bindings.nullableMapping?


It's because of the null check. For mapping I could wrap the function 
parameter in Objects.requireNonNull as it will be evaluated immediately. 
For orElseGet, I had to put the check on a separate line (as the first 
use of supplier parameter is inside a lambda).


I prefer to do null checks as early as possible (so you get a better 
stack trace) but I also prefer to write interface default 
implementations as short as possible. I could write #map to look like 
#orElseGet but it would become one line longer, or I could write 
#orElseGet to do a call to a separate Bindings method and move the null 
check there but then the stack trace for the NPE would no longer point 
to the #orElseGet interface method as first trace line.


I can rewrite this however if you would like, but that's sort of the 
reasoning I had I think when I wrote this code balancing short default 
implementations vs delayed null checks.


If you would want it more consistent, then I think I'd prefer to write 
#map like #orElseGet.



5. I noticed also that subscribeInvalidations in ObservableValue will need
to be hidden.


Yes, that should be no problem. We can either fall back to using 
standard listener management, or create a small helper that works with 
the subscription model.



Some related JBS issues that I found that we might be able to use (or at
least close at some point):
https://bugs.openjdk.java.net/browse/JDK-8091544


To close "Bindings.select*(): add new type-safe template based API 
instead of legacy-style set of methods" we'd need the flatMap/select 
method to be included.


I don't think we really need specialized methods for primitives (or at 
least, not right away).  At this point the primitive versions only 
really differ in what value they'd return if the binding would be null 
and perhaps they do a little less boxing/unboxing. Since you can select 
the default value with #orElse which is more flexible I don't see much 
use to add those variants.



https://bugs.openjdk.java.net/browse/JDK-8091316


"Add a Bindings.map() method".

The ticket is a bit unclear as I can't see what type "x" is.

So this seems to be for a use case where an Observable "x" should be 
bound with some transformation to the windowTitleProperty. I'm assuming 
x is an Observable because it is used in the list of dependencies.


If x is only an Observable, then I don't think the PoC will help.

If x is more (it is hard to tell from the ticket) you might be able to 
map it. However, x seems to have a "getY()" method so it doesn't look 
like it is a Property or ObservableValue...


Making some assumptions here and assuming x has an y property itself, 
you could write this as:


windowsTitleProperty.bind(x.flatMap(XClass::yProperty).map(y -> {
switch(y) {
case ...
}
});

Or:

windowsTitleProperty.bind(x.map(x2 -> {
switch(x2.getY()) {
case ...
}
});

Or perhaps x is the correct property already, then it be:

windowsTitleProperty.bind(x.map(y -> {
switch(y) {
case ...
}
});

--John


On Mon, Sep 13, 2021 at 3:05 AM

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-13 Thread Nir Lisker
Sounds good.

Some points I have (maybe some are premature):

1. I still think that adding the Optional methods for orElse and orElseGet
could be useful. Unless I can be convinced otherwise, I suggest that we be
careful with the naming of current methods that return a binding.
2. I see that in ReactFX the Val.map will pass to MappedVal the mapping
function as-is, and the null check is done in computeValue(). In your
implementation, the LazyBinding (equivalent of MappedVal) is passed a
composite mapping that deals with null and the computeValue() can just use
that new mapping function. I think that the end behavior is the same, but
does your way use more memory for the extra Function lambda?
3. Why does nullableMapping creates an anonymous subtype of LazyBinding,
while flatMapping creates a concrete instance of FlatMapBinding?
4. Why does orElseGet call Bindings.nullableMapping directly, while map
calls Bindings.mapping which in turn calls Bindings.nullableMapping?
5. I noticed also that subscribeInvalidations in ObservableValue will need
to be hidden.

Some related JBS issues that I found that we might be able to use (or at
least close at some point):
https://bugs.openjdk.java.net/browse/JDK-8091544
https://bugs.openjdk.java.net/browse/JDK-8091316

On Mon, Sep 13, 2021 at 3:05 AM John Hendrikx  wrote:

>
>
> On 12/09/2021 02:05, Nir Lisker wrote:
> > I've gotten back to look at this.
> >
> > For now I'm dealing only with the nullableMapping method in Bindings so
> > we can limit the amount of new classes to LazyObjectBinding
> > (FlatMapBinding and ConditionalBinding can come later). This method is
> > used by map, orElse and orElseGet in ObservableValue. Of these, map is
> > the only fundamental one since the other 2 can be represented by it. I
> > don't mind keeping them in the discussion, though I will be centered on
> > the map method.
> >
> > The implementation of these methods rely on Bindings, LazyObjectBinding,
> > and Subscription in the current implementation. I think that we can
> > introduce these internally for now. The biggest hurdle left are the
> > public changes to ObjectBinding. If we add protected methods, we need to
> > be sure that by the end of this large task they would have been the
> > right ones to add and at the right place. This is why I recommend adding
> > them at the package visibility level and add LazyObjectBinding (and
> > friends) in its package so they can extend it. I understand that this
> > can look ugly, but moving internal implementation is cheap, and in this
> > case, since the coupling involves about 3 classes, is very cheap. This
> > will lower the initial integration barrier and let the community get
> > used to- and give feedback on the new changes.
>
> I think that's a good idea, there is no direct need to make those
> protected methods part of the public API as the usefulness of those
> methods will be limited and the major use case will basically be
> provided by LazyObjectBinding already.
>
> > This will leave only 1 change that we are committed to, and that's the
> > new API on ObservableValue (which is the map method in this case). The
> > method looks good; the only question, which has arisen in a few places,
> > is how to handle null. As we discussed here, this method works like its
> > ReactFX counterpart, ignoring null. My questions would be:
> > 1. Is there a good reason to allow null? If so, do we add a new method
> > for it, or do we pass some parameter to the current method to indicate
> that?
>
> In JavaFX, null is something we have to deal in some fashion as
> properties can easily be null. For the "primitive" properties, null (if
> encountered) is translated to a default value. For StringProperty it
> could be an empty string although JavaFX doesn't do this. For
> ObjectProperty there is no sensible default possible.
>
> In ReactFX, nulls are indeed skipped when mapping as it considers null
> to be an empty value, and empty values are skipped according to the
> documentation. The code below will not throw an NPE in the mapping
> function and will simply result in null:
>
>  Var.newSimpleVar(null).map(x -> x + "2").getValue();
>
> This is similar to the PoC implementation:
>
>  new SimpleStringProperty().map(x -> x + "2").getValue());
>
> Having worked with ReactFX and also the PoC, I think it would be very
> cumbersome to have to deal with nulls in mapping functions, as many
> simple mappings expressed with a short lambda would need to deal with
> the null case with a ternary or an if/else block.
>
> In the PoC any mapping you could need that requires mapping null
> explicitely can be expressed in another form:
>
>  .map(x -> x == null ? "empty!" : x + "2")
>
> becomes:
>
>  .map(x -> x + "2").orElse("empty!)
>
> which is not only more concise, but allows to delay dealing with null
> until the very end:
>
>  .map(x -> x == null ? "empty!" : fetchDataWhichCouldBeNull(x))
>  .map(x -> x == null ? "empty!" : x + "2")
>
> ve

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-12 Thread John Hendrikx




On 12/09/2021 02:05, Nir Lisker wrote:

I've gotten back to look at this.

For now I'm dealing only with the nullableMapping method in Bindings so
we can limit the amount of new classes to LazyObjectBinding
(FlatMapBinding and ConditionalBinding can come later). This method is
used by map, orElse and orElseGet in ObservableValue. Of these, map is
the only fundamental one since the other 2 can be represented by it. I
don't mind keeping them in the discussion, though I will be centered on
the map method.

The implementation of these methods rely on Bindings, LazyObjectBinding,
and Subscription in the current implementation. I think that we can
introduce these internally for now. The biggest hurdle left are the
public changes to ObjectBinding. If we add protected methods, we need to
be sure that by the end of this large task they would have been the
right ones to add and at the right place. This is why I recommend adding
them at the package visibility level and add LazyObjectBinding (and
friends) in its package so they can extend it. I understand that this
can look ugly, but moving internal implementation is cheap, and in this
case, since the coupling involves about 3 classes, is very cheap. This
will lower the initial integration barrier and let the community get
used to- and give feedback on the new changes.


I think that's a good idea, there is no direct need to make those 
protected methods part of the public API as the usefulness of those 
methods will be limited and the major use case will basically be 
provided by LazyObjectBinding already.



This will leave only 1 change that we are committed to, and that's the
new API on ObservableValue (which is the map method in this case). The
method looks good; the only question, which has arisen in a few places,
is how to handle null. As we discussed here, this method works like its
ReactFX counterpart, ignoring null. My questions would be:
1. Is there a good reason to allow null? If so, do we add a new method
for it, or do we pass some parameter to the current method to indicate that?


In JavaFX, null is something we have to deal in some fashion as 
properties can easily be null. For the "primitive" properties, null (if 
encountered) is translated to a default value. For StringProperty it 
could be an empty string although JavaFX doesn't do this. For 
ObjectProperty there is no sensible default possible.


In ReactFX, nulls are indeed skipped when mapping as it considers null 
to be an empty value, and empty values are skipped according to the 
documentation. The code below will not throw an NPE in the mapping 
function and will simply result in null:


Var.newSimpleVar(null).map(x -> x + "2").getValue();

This is similar to the PoC implementation:

new SimpleStringProperty().map(x -> x + "2").getValue());

Having worked with ReactFX and also the PoC, I think it would be very 
cumbersome to have to deal with nulls in mapping functions, as many 
simple mappings expressed with a short lambda would need to deal with 
the null case with a ternary or an if/else block.


In the PoC any mapping you could need that requires mapping null 
explicitely can be expressed in another form:


.map(x -> x == null ? "empty!" : x + "2")

becomes:

.map(x -> x + "2").orElse("empty!)

which is not only more concise, but allows to delay dealing with null 
until the very end:


.map(x -> x == null ? "empty!" : fetchDataWhichCouldBeNull(x))
.map(x -> x == null ? "empty!" : x + "2")

versus:

.map(Helper::fetchDataThatCouldBeNull)
.map(x -> x + "2")
.orElse("empty!")

You don't have to delay it though, if for some reason you would want to
map the 2nd null case differently, you could use "orElse" after each
mapping still.

Although for mapping this may seem somewhat contrived, for selecting (or 
flatMapping) where you go through a chain of properties (like 
Node->Scene->Window) being able to delay dealing with nulls leads to 
more concise and IMHO more expressive code.


So in summary, and to answer your first question, I don't think there is 
a good reason to allow null to be passed to mapping functions. We do 
need to deal with nulls though, and that's what "orElse" is for. This 
could also be done with an additional parameter to "map" (a 
"mapOrDefault" similar to "getOrDefault" from the collections API) but I 
think we'd be better served with multiple methods that take a single 
argument as the resulting code is easier to understand especially when 
one of the arguments is a lambda.




2. If we want to replace the Bindings.select (non-type safe) API, can we
do it with our current way of treating null?


In the current Bindings.select API, null is skipped when encountered and 
the resulting value of the chain will be null. This is exactly what 
"flatMap" in the PoC does as well, in other words:


Bindings.select(nodeProperty, "scene", "window", "showing")

is exactly equivalent to:

nodeProperty.flatMap(Node::sceneProperty)
   .

Re: Proof of concept for fluent bindings for ObservableValue

2021-09-11 Thread Nir Lisker
I've gotten back to look at this.

For now I'm dealing only with the nullableMapping method in Bindings so we
can limit the amount of new classes to LazyObjectBinding (FlatMapBinding
and ConditionalBinding can come later). This method is used by map, orElse
and orElseGet in ObservableValue. Of these, map is the only fundamental one
since the other 2 can be represented by it. I don't mind keeping them in
the discussion, though I will be centered on the map method.

The implementation of these methods rely on Bindings, LazyObjectBinding,
and Subscription in the current implementation. I think that we can
introduce these internally for now. The biggest hurdle left are the public
changes to ObjectBinding. If we add protected methods, we need to be sure
that by the end of this large task they would have been the right ones to
add and at the right place. This is why I recommend adding them at the
package visibility level and add LazyObjectBinding (and friends) in its
package so they can extend it. I understand that this can look ugly, but
moving internal implementation is cheap, and in this case, since the
coupling involves about 3 classes, is very cheap. This will lower the
initial integration barrier and let the community get used to- and give
feedback on the new changes.

This will leave only 1 change that we are committed to, and that's the new
API on ObservableValue (which is the map method in this case). The method
looks good; the only question, which has arisen in a few places, is how to
handle null. As we discussed here, this method works like its ReactFX
counterpart, ignoring null. My questions would be:
1. Is there a good reason to allow null? If so, do we add a new method for
it, or do we pass some parameter to the current method to indicate that?
2. If we want to replace the Bindings.select (non-type safe) API, can we do
it with our current way of treating null?

Do you think that this is a valid approach?

- Nir

On Wed, Apr 7, 2021 at 11:30 PM John Hendrikx  wrote:

> On 07/04/2021 03:41, Nir Lisker wrote:
> > In the PoC I made I specifically also disallowed 'null' as an input
> >
> >
> > I like the way ReactFX does it where the property is empty. I think that
> > this is also what you mean by disallowing `null` (in other contexts,
> > "disallowing null" would mean throwing an exception).
>
> Yes, it is the same concept as ReactFX calling a property "empty", but I
> was hesitant to call this as `null` is a valid value for many JavaFX
> properties (a Scene can be null, a String can be null, etc.) which I
> don't think means the same as it being empty (in the Optional sense).
> But as long as the documentation is clear, I don't mind calling it either.
>
> >
> > Not entirely sure what you mean by this.
> >
> >
> > Basically, what you said. My point was that this is a different API
> > section. The first deals with expanding the observables/properties
> > methods. The second with listeners methods. Even if mapping a property
> > requires a new listening model, like subscriptions, this is done under
> > the hood. Exposing this API should be a separate step. At least that's
> > how I see it.
>
> Yes, I think it is good to limit new API as much as possible to reduce
> scope and increase the chances of its acceptance. The subscription parts
> can be designed separately and do not need to be public at this point.
> They can be moved to a helper, or the implementation can take the extra
> effort to use standard listeners.
>
> >
> > I'd be happy to spend more time and work on this. Perhaps it would be
> > possible to collaborate on this?
> >
> >
> > That would be good. I will need to re-review the ReactFX internals and
> > see how your proposal differs exactly.
>
> Yes, I think that would be good to do.
>
> I've done some comparisons myself and didn't find a difference in
> functionality with `Val` (so far). It is a new implementation though, I
> didn't really look at how `Val` was done internally as implementing it
> directly into JavaFX is quite different (I had to make a few minor
> changes in `ObjectBinding` to allow for the choice of lazy binding).  I
> was also initially more focused on Streams only to realize at a later
> point that having a Stream implement ObservableValue was not going to be
> pretty (I suspect this also happened when ReactFX was created, which is
> why Val/Var were later introduced in 2.x).
>
> Both the PoC and Val do lazy binding and are null safe and provide
> methods to deal with null/empty.
>
> The main thing I didn't do yet is provide a `filter` method. Filtering
> properties that you want to use for bindings seems awkard as a binding
> should always have some kind of value. The `filter` method in ReactFX
> basically maps the value to `null` when it doesn't match the filter.
> I've left this out as you can easily achieve this with `map` and
> `filter` seems to be too easy to misunderstand.
>
> Aside from that, ReactFX's Val offers a lot of other methods that are

Re: Proof of concept for fluent bindings for ObservableValue

2021-04-07 Thread John Hendrikx

On 07/04/2021 03:41, Nir Lisker wrote:

In the PoC I made I specifically also disallowed 'null' as an input


I like the way ReactFX does it where the property is empty. I think that
this is also what you mean by disallowing `null` (in other contexts,
"disallowing null" would mean throwing an exception).


Yes, it is the same concept as ReactFX calling a property "empty", but I 
was hesitant to call this as `null` is a valid value for many JavaFX 
properties (a Scene can be null, a String can be null, etc.) which I 
don't think means the same as it being empty (in the Optional sense). 
But as long as the documentation is clear, I don't mind calling it either.




Not entirely sure what you mean by this.


Basically, what you said. My point was that this is a different API
section. The first deals with expanding the observables/properties
methods. The second with listeners methods. Even if mapping a property
requires a new listening model, like subscriptions, this is done under
the hood. Exposing this API should be a separate step. At least that's
how I see it.


Yes, I think it is good to limit new API as much as possible to reduce 
scope and increase the chances of its acceptance. The subscription parts 
can be designed separately and do not need to be public at this point. 
They can be moved to a helper, or the implementation can take the extra 
effort to use standard listeners.




I'd be happy to spend more time and work on this. Perhaps it would be
possible to collaborate on this?


That would be good. I will need to re-review the ReactFX internals and
see how your proposal differs exactly.


Yes, I think that would be good to do.

I've done some comparisons myself and didn't find a difference in 
functionality with `Val` (so far). It is a new implementation though, I 
didn't really look at how `Val` was done internally as implementing it 
directly into JavaFX is quite different (I had to make a few minor 
changes in `ObjectBinding` to allow for the choice of lazy binding).  I 
was also initially more focused on Streams only to realize at a later 
point that having a Stream implement ObservableValue was not going to be 
pretty (I suspect this also happened when ReactFX was created, which is 
why Val/Var were later introduced in 2.x).


Both the PoC and Val do lazy binding and are null safe and provide 
methods to deal with null/empty.


The main thing I didn't do yet is provide a `filter` method. Filtering 
properties that you want to use for bindings seems awkard as a binding 
should always have some kind of value. The `filter` method in ReactFX 
basically maps the value to `null` when it doesn't match the filter. 
I've left this out as you can easily achieve this with `map` and 
`filter` seems to be too easy to misunderstand.


Aside from that, ReactFX's Val offers a lot of other methods that are I 
think a bit too specialized to consider at this point, like the 
`animate`, `pin`, `mapDynamic` and `suspendable` methods.


Val also has all the other `Optional` methods (ifPresent, isPresent, 
isEmpty) but I think they may make the API a bit confusing (an 
observable value is not the same as an optional). I've also not had a 
need for these so far in practice and you can easily convert the current 
value to get this functionality with `Optional.ofNullable`.


Finally Val offers a few methods to convert to ReactFX's streams. While 
convenient, I think static methods like `Values.of`, `Invalidations.of` 
or `Changes.of` would make for a less cluttered API to do stream 
conversions -- this would also make it possible to leave this part of 
the API up to a 3rd party.



 By the way, do you make a distinction between ReactFX's Val and Var in
your proposal (one being read-only)?


No, `ObservableValue` is basically the same as `Val`, and the equivalent 
to `Var` is `ObjectProperty`.  Aside from it being a good companion to 
`Val` (and less typing), I don't see a reason to implement `Var`.


--John



On Sun, Apr 4, 2021 at 12:43 PM John Hendrikx mailto:hj...@xs4all.nl>> wrote:



On 02/04/2021 08:47, Nir Lisker wrote:
> Hi John,
>
> I've had my eyes set on ReactFX enhancements for a while too,
especially as
> a replacement for the unsafe "select" mechanism. One of the things
that
> kept me from going forward with this is seeing what Valhalla will
bring.
> Generic specialization might save a lot of duplication work on
something
> like this, and Tomas touched another related issue [1], but since
it could
> be a long time before that happens, it's worth planning what we
can extract
> from ReactFX currently.

Agreed, Valhalla is certainly a highly anticipated feature but I
fear it
is still a couple of years away.

Even without any initial support for dealing with "? extends Number"
from the various ObservableValue specializations I think looking into
this can already be tremendous help.

The proof of conc

Re: Proof of concept for fluent bindings for ObservableValue

2021-04-06 Thread Nir Lisker
>
> In the PoC I made I specifically also disallowed 'null' as an input
>

I like the way ReactFX does it where the property is empty. I think that
this is also what you mean by disallowing `null` (in other contexts,
"disallowing null" would mean throwing an exception).

Not entirely sure what you mean by this.
>

Basically, what you said. My point was that this is a different API
section. The first deals with expanding the observables/properties methods.
The second with listeners methods. Even if mapping a property requires a
new listening model, like subscriptions, this is done under the hood.
Exposing this API should be a separate step. At least that's how I see it.

I'd be happy to spend more time and work on this. Perhaps it would be
> possible to collaborate on this?
>

That would be good. I will need to re-review the ReactFX internals and see
how your proposal differs exactly.

 By the way, do you make a distinction between ReactFX's Val and Var in
your proposal (one being read-only)?

On Sun, Apr 4, 2021 at 12:43 PM John Hendrikx  wrote:

>
>
> On 02/04/2021 08:47, Nir Lisker wrote:
> > Hi John,
> >
> > I've had my eyes set on ReactFX enhancements for a while too, especially
> as
> > a replacement for the unsafe "select" mechanism. One of the things that
> > kept me from going forward with this is seeing what Valhalla will bring.
> > Generic specialization might save a lot of duplication work on something
> > like this, and Tomas touched another related issue [1], but since it
> could
> > be a long time before that happens, it's worth planning what we can
> extract
> > from ReactFX currently.
>
> Agreed, Valhalla is certainly a highly anticipated feature but I fear it
> is still a couple of years away.
>
> Even without any initial support for dealing with "? extends Number"
> from the various ObservableValue specializations I think looking into
> this can already be tremendous help.
>
> The proof of concept mainly requires you convert the Number to a
> suitable type when reading the property but has no problems in the other
> direction:
>
>  label.widthProperty().map(Number::doubleValue).map(x -> x + 1);
>
> Not pretty, but certainly workable. Specific methods could be introduced
> (even at a later time) to make this more streamlined, similar to what
> the Stream API offers with 'mapToDouble' etc.
>
> > I think that we should break the enhancements into parts.
> > The first that I would advise to look at are the additions to
> > properties/observables. Tomas had to create Val and Var because he
> couldn't
> > change the core interfaces, but we can. Fitting them with the Optional
> > methods like `isPresent`, `isEmpty`, `ifPresent`, `map`. `flatMap` etc.;
> > and `select` and friends, is already a good start that will address many
> > common requirements.
>
> Yes, Val/Var had to be created for that reason, and also because
> properties don't quite behave the same as streams -- streams with a
> "toBinding" method results in things people didn't quite expect.
>
> As far as the Optional methods go, I'm not entirely sure properties
> would benefit from all of them. Properties are not immutable like
> Optional and it may make less sense to fit them with 'isPresent',
> 'isEmpty' and 'ifPresent' ('ifPresent' would I think need to behave
> similar to 'addListener' or 'subscribe').
>
> In the PoC I made I specifically also disallowed 'null' as an input for
> functions like 'map' and 'flatMap' (opting to use 'orElse' semantics for
> 'null'), as this for allows much cleaner mapping (and especially flat
> mapping when selecting nested properties). If 'null' were to be allowed,
> I think at a minimum we'd need to add another method to allow for easy
> selecting of nested properties to avoid:
>
>  obs.flatMap(x -> x == null ? null : x.otherProperty())
>
> > The second part is related to listeners. The subscription model and event
> > streams try to solve the memory issues with hard and weak references, and
> > allow better composition.
>
> Not entirely sure what you mean by this. JavaFX's current model uses
> weak references which was I think an unfortunate decision as it can
> result in huge confusion.  For example, a direct binding will work, but
> with an indirection step a binding stops working:
>
>  button.textProperty()
> .concat("World")  // weak binding used here
> .addListener((obs, old, cur) -> System.out.println(cur));
>
> The above stops working, but without the 'concat' it keeps working.
>
> I think the use of weak listeners should be avoided and instead other
> mechanisms should be provided to make cleaning up easier. This is the
> main reason for 'conditionOn' and why ReactFX even had a specialized
> version of it: 'conditionOnShowing(Node)'.
>
> > The third part is for collections - things like transformation lists
> > (LiveList) and for other collections.
>
> This is indeed best saved for last. The problems there I think are less
> of an issue for now.
>
> > Since thes

Re: Proof of concept for fluent bindings for ObservableValue

2021-04-04 Thread John Hendrikx




On 02/04/2021 08:47, Nir Lisker wrote:

Hi John,

I've had my eyes set on ReactFX enhancements for a while too, especially as
a replacement for the unsafe "select" mechanism. One of the things that
kept me from going forward with this is seeing what Valhalla will bring.
Generic specialization might save a lot of duplication work on something
like this, and Tomas touched another related issue [1], but since it could
be a long time before that happens, it's worth planning what we can extract
from ReactFX currently.


Agreed, Valhalla is certainly a highly anticipated feature but I fear it 
is still a couple of years away.


Even without any initial support for dealing with "? extends Number" 
from the various ObservableValue specializations I think looking into 
this can already be tremendous help.


The proof of concept mainly requires you convert the Number to a 
suitable type when reading the property but has no problems in the other 
direction:


label.widthProperty().map(Number::doubleValue).map(x -> x + 1);

Not pretty, but certainly workable. Specific methods could be introduced 
(even at a later time) to make this more streamlined, similar to what 
the Stream API offers with 'mapToDouble' etc.



I think that we should break the enhancements into parts.
The first that I would advise to look at are the additions to
properties/observables. Tomas had to create Val and Var because he couldn't
change the core interfaces, but we can. Fitting them with the Optional
methods like `isPresent`, `isEmpty`, `ifPresent`, `map`. `flatMap` etc.;
and `select` and friends, is already a good start that will address many
common requirements.


Yes, Val/Var had to be created for that reason, and also because 
properties don't quite behave the same as streams -- streams with a 
"toBinding" method results in things people didn't quite expect.


As far as the Optional methods go, I'm not entirely sure properties 
would benefit from all of them. Properties are not immutable like 
Optional and it may make less sense to fit them with 'isPresent', 
'isEmpty' and 'ifPresent' ('ifPresent' would I think need to behave 
similar to 'addListener' or 'subscribe').


In the PoC I made I specifically also disallowed 'null' as an input for 
functions like 'map' and 'flatMap' (opting to use 'orElse' semantics for 
'null'), as this for allows much cleaner mapping (and especially flat 
mapping when selecting nested properties). If 'null' were to be allowed, 
I think at a minimum we'd need to add another method to allow for easy 
selecting of nested properties to avoid:


obs.flatMap(x -> x == null ? null : x.otherProperty())


The second part is related to listeners. The subscription model and event
streams try to solve the memory issues with hard and weak references, and
allow better composition.


Not entirely sure what you mean by this. JavaFX's current model uses 
weak references which was I think an unfortunate decision as it can 
result in huge confusion.  For example, a direct binding will work, but 
with an indirection step a binding stops working:


button.textProperty()
   .concat("World")  // weak binding used here
   .addListener((obs, old, cur) -> System.out.println(cur));

The above stops working, but without the 'concat' it keeps working.

I think the use of weak listeners should be avoided and instead other 
mechanisms should be provided to make cleaning up easier. This is the 
main reason for 'conditionOn' and why ReactFX even had a specialized 
version of it: 'conditionOnShowing(Node)'.



The third part is for collections - things like transformation lists
(LiveList) and for other collections.


This is indeed best saved for last. The problems there I think are less 
of an issue for now.



Since these share behavior under the hood, we need to look ahead, but in
terms of functionality, I think we should take smaller steps. It will also
be easier to propose these then.


I've for this reason kept the PoC small with only the most basic 
functionality.  I did however add some work for a different subscription 
model, mainly because the internals of this code benefits greatly from 
it. It is however kept to a minimum.


I'd be happy to spend more time and work on this. Perhaps it would be 
possible to collaborate on this?


--John



- Nir

[1]
https://github.com/TomasMikula/ReactFX/wiki/Creating-a-Val-or-Var-Instance#the-javafx-propertynumber-implementation-issue

On Wed, Mar 24, 2021 at 11:49 PM John Hendrikx  wrote:


I just wanted to draw some attention to a recent proof of concept I made
in this pull request: https://github.com/openjdk/jfx/pull/434

It is based on the work I did in
https://github.com/hjohn/hs.jfx.eventstream which is in part based on
work done in ReactFX by Tomas Mikula. The PR itself however shares no
code with ReactFX and is
completely written by me.

If there is interest, I'm willing to invest more time in smoothing out
the API and documentation, investigating further how this 

Re: Proof of concept for fluent bindings for ObservableValue

2021-04-01 Thread Nir Lisker
Hi John,

I've had my eyes set on ReactFX enhancements for a while too, especially as
a replacement for the unsafe "select" mechanism. One of the things that
kept me from going forward with this is seeing what Valhalla will bring.
Generic specialization might save a lot of duplication work on something
like this, and Tomas touched another related issue [1], but since it could
be a long time before that happens, it's worth planning what we can extract
from ReactFX currently.

I think that we should break the enhancements into parts.
The first that I would advise to look at are the additions to
properties/observables. Tomas had to create Val and Var because he couldn't
change the core interfaces, but we can. Fitting them with the Optional
methods like `isPresent`, `isEmpty`, `ifPresent`, `map`. `flatMap` etc.;
and `select` and friends, is already a good start that will address many
common requirements.
The second part is related to listeners. The subscription model and event
streams try to solve the memory issues with hard and weak references, and
allow better composition.
The third part is for collections - things like transformation lists
(LiveList) and for other collections.

Since these share behavior under the hood, we need to look ahead, but in
terms of functionality, I think we should take smaller steps. It will also
be easier to propose these then.

- Nir

[1]
https://github.com/TomasMikula/ReactFX/wiki/Creating-a-Val-or-Var-Instance#the-javafx-propertynumber-implementation-issue

On Wed, Mar 24, 2021 at 11:49 PM John Hendrikx  wrote:

> I just wanted to draw some attention to a recent proof of concept I made
> in this pull request: https://github.com/openjdk/jfx/pull/434
>
> It is based on the work I did in
> https://github.com/hjohn/hs.jfx.eventstream which is in part based on
> work done in ReactFX by Tomas Mikula. The PR itself however shares no
> code with ReactFX and is
> completely written by me.
>
> If there is interest, I'm willing to invest more time in smoothing out
> the API and documentation, investigating further how this would interact
> with the primitive types and adding unit test coverage (I have extensive
> tests, but thesea are written in JUnit 5, so they would require
> conversion or JavaFX could move to support JUnit 5).
>
> What follows below is the text of the PR for easy reading. Feedback is
> appreciated.
>
> 
>
> This is a proof of concept of how fluent bindings could be introduced to
> JavaFX. The main benefit of fluent bindings are ease of use, type safety
> and less surprises. Features:
>
> Flexible Mappings
> Map the contents of a property any way you like with map, or map nested
> properties with flatMap.
>
> Lazy
> The bindings created are lazy, which means they are always invalid when
> not themselves observed. This allows for easier garbage collection (once
> the last observer is removed, a chain of bindings will stop observing
> their parents) and less listener management when dealing with nested
> properties. Furthermore, this allows inclusion of such bindings in
> classes such as Node without listeners being created when the binding
> itself is not used (this would allow for the inclusion of a
> treeShowingProperty in Node without creating excessive listeners, see
> this fix I did in an earlier PR: #185)
>
> Null Safe
> The map and flatMap methods are skipped, similar to java.util.Optional
> when the value they would be mapping is null. This makes mapping nested
> properties with flatMap trivial as the null case does not need to be
> taken into account in a chain like this:
> node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty).
>
> Instead a default can be provided with orElse or orElseGet.
>
> Conditional Bindings
> Bindings can be made conditional using the conditionOn method. A
> conditional binding retains its last value when its condition is false.
> Conditional bindings donot observe their source when the condition is
> false, allowing developers to automatically stop listening to properties
> when a certain condition is met. A major use of this feature is to have
> UI components that need to keep models updated which may outlive the UI
> conditionally update the long lived model only when the UI is showing.
>
> Some examples:
>
> void mapProperty() {
>// Standard JavaFX:
>label.textProperty().bind(Bindings.createStringBinding(() ->
> text.getValueSafe().toUpperCase(), text));
>
>// Fluent: much more compact, no need to handle null
>label.textProperty().bind(text.map(String::toUpperCase));
> }
>
> void calculateCharactersLeft() {
>// Standard JavaFX:
>
> label.textProperty().bind(text.length().negate().add(100).asString().concat("
>
> characters left"));
>
>// Fluent: slightly more compact and more clear (no negate needed)
>label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() +
> " characters left"));
> }
>
> void mapNestedValue() {
>// Standard JavaFX:
>labe

Proof of concept for fluent bindings for ObservableValue

2021-03-24 Thread John Hendrikx
I just wanted to draw some attention to a recent proof of concept I made 
in this pull request: https://github.com/openjdk/jfx/pull/434


It is based on the work I did in 
https://github.com/hjohn/hs.jfx.eventstream which is in part based on 
work done in ReactFX by Tomas Mikula. The PR itself however shares no 
code with ReactFX and is

completely written by me.

If there is interest, I'm willing to invest more time in smoothing out 
the API and documentation, investigating further how this would interact 
with the primitive types and adding unit test coverage (I have extensive 
tests, but thesea are written in JUnit 5, so they would require 
conversion or JavaFX could move to support JUnit 5).


What follows below is the text of the PR for easy reading. Feedback is 
appreciated.




This is a proof of concept of how fluent bindings could be introduced to 
JavaFX. The main benefit of fluent bindings are ease of use, type safety 
and less surprises. Features:


Flexible Mappings
Map the contents of a property any way you like with map, or map nested 
properties with flatMap.


Lazy
The bindings created are lazy, which means they are always invalid when 
not themselves observed. This allows for easier garbage collection (once 
the last observer is removed, a chain of bindings will stop observing 
their parents) and less listener management when dealing with nested 
properties. Furthermore, this allows inclusion of such bindings in 
classes such as Node without listeners being created when the binding 
itself is not used (this would allow for the inclusion of a 
treeShowingProperty in Node without creating excessive listeners, see 
this fix I did in an earlier PR: #185)


Null Safe
The map and flatMap methods are skipped, similar to java.util.Optional 
when the value they would be mapping is null. This makes mapping nested 
properties with flatMap trivial as the null case does not need to be 
taken into account in a chain like this: 
node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty). 
Instead a default can be provided with orElse or orElseGet.


Conditional Bindings
Bindings can be made conditional using the conditionOn method. A 
conditional binding retains its last value when its condition is false. 
Conditional bindings donot observe their source when the condition is 
false, allowing developers to automatically stop listening to properties 
when a certain condition is met. A major use of this feature is to have 
UI components that need to keep models updated which may outlive the UI 
conditionally update the long lived model only when the UI is showing.


Some examples:

void mapProperty() {
  // Standard JavaFX:
  label.textProperty().bind(Bindings.createStringBinding(() -> 
text.getValueSafe().toUpperCase(), text));


  // Fluent: much more compact, no need to handle null
  label.textProperty().bind(text.map(String::toUpperCase));
}

void calculateCharactersLeft() {
  // Standard JavaFX:

label.textProperty().bind(text.length().negate().add(100).asString().concat(" 
characters left"));


  // Fluent: slightly more compact and more clear (no negate needed)
  label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() + 
" characters left"));

}

void mapNestedValue() {
  // Standard JavaFX:
  label.textProperty().bind(Bindings.createStringBinding(
() -> employee.get() == null ? ""
: employee.get().getCompany() == null ? ""
: employee.get().getCompany().getName(),
employee
  ));

  // Fluent: no need to handle nulls everywhere
  label.textProperty().bind(
employee.map(Employee::getCompany)
.map(Company::getName)
.orElse("")
  );
}

void mapNestedProperty() {
  // Standard JavaFX:
  label.textProperty().bind(
Bindings.when(Bindings.selectBoolean(label.sceneProperty(), 
"window", "showing"))

  .then("Visible")
  .otherwise("Not Visible")
  );

  // Fluent: type safe
  label.textProperty().bind(label.sceneProperty()
.flatMap(Scene::windowProperty)
.flatMap(Window::showingProperty)
.orElse(false)
.map(showing -> showing ? "Visible" : "Not Visible")
  );
}

void updateLongLivedModelWhileAvoidingMemoryLeaks() {
  // Standard JavaFX: naive, memory leak; UI won't get garbage collected
  listView.getSelectionModel().selectedItemProperty().addListener(
(obs, old, current) -> 
longLivedModel.lastSelectedProperty().set(current)

  );

  // Standard JavaFX: no leak, but stops updating after a while
  listView.getSelectionModel().selectedItemProperty().addListener(
new WeakChangeListener<>(
  (obs, old, current) -> 
longLivedModel.lastSelectedProperty().set(current)

)
  );

  // Standard JavaFX: fixed version
  listenerReference = (obs, old, current) -> 
longLivedModel.lastSelectedProperty().set(current);


  listView.getSelectionModel().selectedItemProperty().addListener(
new WeakChangeListener<>(listenerReference)
  );

  // Fluent: naive, memory leak... flu