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 <ebre...@gmail.com> 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 <nlis...@gmail.com
> (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 (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 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 calling #getValue is fine without much need
> > > for another #orElse variant.
> > >
> > > --John
> > >
> > > >
> > > > [1]
> > > >
> > >
> https://github.com/openjdk/jfx-sandbox/tree/jfx-sandbox/nlisker_fuent_bindings
> > > >
> > > > On Wed, Sep 15, 2021 at 1:59 PM John Hendrikx <hj...@xs4all.nl
> (mailto:hj...@xs4all.nl)> 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<T>. 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<? extends T> defaultSupplier)
> > > > > > Val<T> orElseConst(T other)
> > > > > > Val<T> orElse(ObservableValue<T> 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<T>, but they have the same name.
> > > > >
> > > > > So java.util.Optional offers:
> > > > >
> > > > > T orElse(T other)
> > > > > T orElseGet(Supplier<? extends T> supplier)
> > > > >
> > > > > And the PoC:
> > > > >
> > > > > ObservableValue<T> orElse(T alternativeValue)
> > > > > ObservableValue<T> orElseGet(Supplier<? extends T> 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<T> orElse(ObservableValue<T> other)
> > > > >
> > > > > This is another rarely used feature IMHO, and I think Optional#or
> would
> > > > > a better match for this functionality:
> > > > >
> > > > > Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
> > > > >
> > > > > In the POC the signature would be:
> > > > >
> > > > > ObservableValue<T> or(
> > > > > Supplier<? extends ObservableValue<? extends T>> 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).
> > > > >
> > > > > Agreed.
> > > > >
> > > > > --John
> > > > >
> > > >
> > >
>

Reply via email to