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

Reply via email to