Yep that's correct. You cant get strong typing with varargs. Overloading
(yes, lazy) is how I handle it right now. I believe there's really one 2
methods that do anything significant to accomplish the goal, one that calls
a single nested function, and one that calls a single nested BiConsumer.
The rest essentially just chain on top of those.

More thought could certainly be given to it. There may be other related use
cases I haven't encountered. And I've yet to need nesting beyond 3 levels,
but would  probaby offer methods that go a few levels beyond that, if only
because the cost is very little.

On Sun, Aug 6, 2023, 5:49 AM Rob Spoor <apa...@icemanx.nl> wrote:

> I don't think that function chaining with varargs works, except with
> UnaryOperator. After all, the output type of the first must be
> compatible with the input type of the second, the output type of the
> second must be compatible with the input type of the third, etc.
>
> If you want to continue this way, the best option would be to have some
> overloads:
>
>      <T, I, R> Function<T, R> nested(
>              Function<? super T, ? extends I> first,
>              Function<? super I, ? extends R> second)
>      <T, I, R> Function<T, R> nested(
>              Function<? super T, ? extends I> first,
>              Function<? super I, ? extends R> second,
>              R defaultValue)
>      <T, I1, I2, R> Function<T, R> nested(
>              Function<? super T, ? extends I1> first,
>              Function<? super I1, ? extends I2> second,
>              Function<? super I2, ? extends R> third)
>      <T, I1, I2, R> Function<T, R> nested(
>              Function<? super T, ? extends I1> first,
>              Function<? super I1, ? extends I2> second,
>              Function<? super I2, ? extends R> third,
>              R defaultValue)
>      ...
>
> If you're lazy you can delegate the overload with N functions to the
> overload with N-1 functions:
>
>      <T, I1, I2, I3, R> Function<T, R> nested(
>              Function<? super T, ? extends I1> first,
>              Function<? super I1, ? extends I2> second,
>              Function<? super I2, ? extends I3> third,
>              Function<? super I3, ? extends R> fourth,
>              R defaultValue) {
>
>          return nested(first, nested(second, third, fourth),
>                  defaultValue);
>      }
>
>
> Rob
>
>
> On 06/08/2023 01:28, Gary Gregory wrote:
> > I'm not sure the "nested" example API is quite what it should be, because
> > the last argument is the default value, you cannot make the input
> functions
> > a vararg, which seems very limiting. I should be able to use the same API
> > whether I need to go 1, 2, or N functions deep. I'm saying the above
> > independently of whether this type of code should be in Lang.
> >
> > Gary
> >
> > On Sat, Aug 5, 2023, 9:27 AM Daniel Watson <dcwatso...@gmail.com> wrote:
> >
> >> Nice.
> >>
> >> Sounds like everyone is leaning towards "no". Would it be worth
> submitting
> >> a PR to include more usage examples - which I assume could also serve
> as a
> >> place to collect more feedback? Or just keep it within this thread given
> >> the way it's leaning? (or unless that consensus changes)
> >>
> >> Ultimately in my web/UI project the reduction (after using
> function(...))
> >> is something like...
> >>
> >> Failable.asFunction(Parent::getChild)
> >> .andThen(Optional::ofNullable)
> >> .andThen(o -> o.map(Child::getGrandChild))
> >> .andThen(o-> o.map(GrandChild::getName).orElse(defaultValue));
> >>
> >> vs my util method
> >>
> >> FunctionUtils.nested(Parent::getChild, Child::getGrandChild,
> >> GrandChild::getName, defaultValue);
> >>
> >> So it's still a big difference in clarity for me, given how often its
> used.
> >> FWIW - My project is using Vaadin, and this util function is used to
> bind
> >> nested bean properties to Vaadin input fields. On that note - In
> addition
> >> to the bean "getter" binding, it also uses a similar util method to bind
> >> bean "setter" methods - because input fields obviously need access to
> both.
> >> The setter util call looks similar, with the last argument being
> >> a BiConsumer...
> >>
> >> FunctionUtils.nested(Parent::getChild, Child::getGrandChild,
> >> GrandChild::setName);
> >>
> >> Although in general this code does not reference any Vaadin specific
> >> functionality, the overall use case may be quite specific to those
> needs,
> >> so all of these utilities may be better suited to a utils class within a
> >> vaadin specific library.
> >>
> >> Dan
> >>
> >> On Fri, Aug 4, 2023 at 9:11 PM Gary Gregory <garydgreg...@gmail.com>
> >> wrote:
> >>
> >>> The function() method is a great technique, it's now in Functions and
> >>> FailableFunction (git master).
> >>>
> >>> I'll see later if it can be used within Lang. I know I can use it in
> >> other
> >>> projects.
> >>>
> >>> Wrt an API for a vararg of functions that implements chaining
> internally,
> >>> I'm not so sure. I've though I needed something like that in past, but
> >> I've
> >>> always ended up with other coding patterns I found better at the time
> for
> >>> whatever reason..
> >>>
> >>> Gary
> >>>
> >>> Gary
> >>>
> >>> On Fri, Aug 4, 2023, 3:24 PM Gary Gregory <garydgreg...@gmail.com>
> >> wrote:
> >>>
> >>>> Worth adding adding function(Function)? Seems low cost to add it
> >>>> FailableFunction.
> >>>>
> >>>> Gary
> >>>>
> >>>> On Fri, Aug 4, 2023, 2:04 PM Rob Spoor <apa...@icemanx.nl> wrote:
> >>>>
> >>>>> With just one simple utility method you can get all the chaining you
> >>> want:
> >>>>>
> >>>>>       public static <T, R> Function<T, R> function(Function<T, R>
> >> func) {
> >>>>>           return func;
> >>>>>       }
> >>>>>
> >>>>> This doesn't look very useful, but it allows you to turn a method
> >>>>> reference or lambda into a typed Function without needing a cast.
> >> After
> >>>>> that it's really simple using what's provided in the Java API:
> >>>>>
> >>>>>       Function<MyBean, String> func = function(MyBean::getChild)
> >>>>>               .andThen(Child::getName);
> >>>>>
> >>>>> You want a default value? Almost just as easy:
> >>>>>
> >>>>>       someFrameworkThing.setProperty(function(ParentBean::getChild)
> >>>>>               .andThen(ChildBean::getName)
> >>>>>               .andThen(Optional::ofNullable)
> >>>>>               .andThen(o -> o.orElse("defaultName"));
> >>>>>
> >>>>>
> >>>>> On 04/08/2023 16:04, Daniel Watson wrote:
> >>>>>> Asking for comments and thoughts on a potential new feature. Already
> >>>>>> developed in a commons-like style, but dont want to submit PR
> >> without
> >>>>>> discussion as it may be considered out of scope or too use case
> >>>>> specific.
> >>>>>>
> >>>>>> Justification and details...
> >>>>>>
> >>>>>> I've run into a scenario a few times where nested lamba functions
> >>> would
> >>>>> be
> >>>>>> incredibly useful. e.g.
> >>>>>>
> >>>>>> MyBean::getChild::getName
> >>>>>>
> >>>>>> Obviously this is not a language feature, but can be simulated in a
> >>>>> useful
> >>>>>> way. So far my use has mostly been related to code that works with
> >>> POJO
> >>>>>> beans, and frameworks that use function references to understand
> >> those
> >>>>>> beans and properties. Specifically useful where the context of the
> >>> code
> >>>>>> block is the parent entity, but you need to reference a child, and
> >>>>> without
> >>>>>> nested lambdas you end up with things like the below...
> >>>>>>
> >>>>>> ParentBean parentBean = new ParentBean();
> >>>>>> parentBean.setChild(new ChildBean("name"));
> >>>>>> //imagine that FrameworkThing is a generic class, and thus the
> >> generic
> >>>>> type
> >>>>>> is ParentBean
> >>>>>> FrameworkThing someFrameworkThing = new FrameworkThing
> >>>>> (ParentBean.class)
> >>>>>> //but we need to get to a property of a child bean
> >>>>>> someFrameworkThing.setProperty((parentBean) ->  {
> >>>>>>
> >>>>>> return parentBean.getChild().getName();
> >>>>>>
> >>>>>> });
> >>>>>>
> >>>>>> Obviously this could be handled with a getChildName() method on the
> >>>>> parent
> >>>>>> bean, but that has pitfalls as well (e.g. bean class cannot be
> >>> changed,
> >>>>> or
> >>>>>> adding of properties interferes with other usage of the class e.g.
> >>> JPA,
> >>>>>> JAX).  However with a util class the second call can be reduced to
> >>>>>> something like below, leaving the bean API untouched.
> >>>>>>
> >>>>>>
> >>>>>
> >>>
> >>
> someFrameworkThing.setProperty(FunctionUtils.nested(ParentBean::getChild,ChildBean::getName));
> >>>>>>
> >>>>>> Taken alone, that single reduction may seem trivial, but in a
> >> scenario
> >>>>>> where these nested references are commonly needed, the reduction
> >> makes
> >>>>> the
> >>>>>> code clearer (In my opinion), as it is immediately apparent on a
> >>> single
> >>>>>> line of code that the reference is a simple nested property, rather
> >>> than
> >>>>>> having to interpret an inline lambda function. It also discourages
> >>>>> errant
> >>>>>> placement of code by avoiding the inline function (since the only
> >>>>> purpose
> >>>>>> of the lambda was to retrieve a single nested value). In addition,
> >> If
> >>>>>> intermediate nulls need to be handled then the reduction becomes
> >> more
> >>>>>> apparent, as the null checks can be handled in the util class rather
> >>>>> than
> >>>>>> cluttering the app code. e.g.
> >>>>>>
> >>>>>>
> >>>>>
> >>>
> >>
> someFrameworkThing.setProperty(FunctionUtils.nested(ParentBean::getChild,ChildBean::getName,"defaultName"));
> >>>>>> //or...
> >>>>>>
> >>>>>
> >>>
> >>
> someFrameworkThing.setProperty(FunctionUtils.nested(ParentBean::getChild,ChildBean::getName,null));
> >>>>>>
> >>>>>> The third parameter here is a String (typed genetically based on the
> >>>>> return
> >>>>>> type of getName) and indicates the default value to be returned if
> >> the
> >>>>>> first call to getChild() returns null. e.g. it replaces something
> >>>>> like...
> >>>>>>
> >>>>>> someFrameworkThing.setProperty((parentBean) ->  {
> >>>>>>
> >>>>>> ChildBean cb = parentBean.getChild();
> >>>>>> if(cb == null) return null; //or other default value
> >>>>>> else return cb.getName();
> >>>>>>
> >>>>>> });
> >>>>>>
> >>>>>> Given that commons-lang aims to extend existing language features,
> >>> this
> >>>>>> seemed like a reasonable place for a nested lambda util class. So
> >> far
> >>> my
> >>>>>> concerns are...
> >>>>>>
> >>>>>>      1. Does this feel too specific to an application to warrant
> >>>>> inclusion in
> >>>>>>      commons? (For me it has been useful enough to place into a
> >> common
> >>>>> library,
> >>>>>>      but commons-lang has a broader scope)
> >>>>>>      2. If not commons-lang, is there some other commons library
> that
> >>>>> this is
> >>>>>>      more suited to?
> >>>>>>      3. There are still wrinkles that may prove complex and
> >> potentially
> >>>>>>      overly specific e.g. exception handling. Does that potential
> >>>>> complexity
> >>>>>>      make it not worth adding?
> >>>>>>      4. Assuming the features discussed here *are* valuable, Is
> >>> handling
> >>>>> only
> >>>>>>      java.util.Function a complete-enough feature? Or is it useless
> >>>>> unless it
> >>>>>>      also attempts to handle BiFunctions - which become increasingly
> >>>>> complex
> >>>>>>      (potentially unfeasible) to implement - i.e. is it too big a
> >>>>> feature to
> >>>>>>      consider including?
> >>>>>>
> >>>>>> If folks feel like this is a solid "no" let me know. If the devil is
> >>> in
> >>>>> the
> >>>>>> details and we need to see the PR first I can do that as well.
> >>>>>>
> >>>>>> Dan
> >>>>>>
> >>>>>
> >>>>>
> >>>>> ---------------------------------------------------------------------
> >>>>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
> >>>>> For additional commands, e-mail: dev-h...@commons.apache.org
> >>>>>
> >>>>>
> >>>
> >>
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
> For additional commands, e-mail: dev-h...@commons.apache.org
>
>

Reply via email to