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