Add Predicate.of(), Consumer.of(), etc.

Remi Forax forax at univ-mlv.fr
Thu May 7 19:26:32 UTC 2015



On 05/06/2015 03:53 PM, Paul Sandoz wrote:
> On May 6, 2015, at 1:58 PM, Attila Szegedi <attila.szegedi at oracle.com> wrote:
>
>> On May 6, 2015, at 12:37 PM, Paul Sandoz <paul.sandoz at oracle.com> wrote:
>>>
>>> On May 2, 2015, at 11:31 PM, Remi Forax <forax at univ-mlv.fr> wrote:
>>>
>>>> Hi all,
>>>> today, I stubble on a variant of JDK-8050818 [1],
>>>> trying to call negate() on a lambda which is not yet a Predicate (due to target typing) which requires either to cast the lambda to a Predicate and everybody knows that cast are evil or to add a local variable.
>>>>
>>>> I think there is a way to fix that, it's not very orthodox so I let you judge.
>>>> The idea is to introduce a static method 'of' on Predicate,
>>>> class Predicate<T> {
>>>>   ...
>>>>   public static <T> Predicate<T> of(Predicate<T> predicate) {
>>>>     return predicate;
>>>>   }
>>>> }
>>>>
>>>> so one can write:
>>>> stream.filter(Predicate.of(String::isEmpty).negate())
>>>> compared to
>>>> stream.filter(((Predicate<String>)String::isEmpty).negate())
>>>>
>>>> so the question is, does it it worth the cost to introduce a static method that basically do nothing on all functional interfaces of java.util.function.
>>>>
>>> That does have the virtue of not adding a static method per operation but i still cannot bring myself to add such methods as a work around for seems like a compiler inference problem (albeit one in which might be very tricky or not possible to solve).
>> I think it is firmly in the category of “very tricky”, but probably still possible, albeit (from my point of view) undesirable.
>>
>> String::isEmpty will not, on its own, have a method named .negate(); the compiler can’t really infer the programmer’s intent to make it into a Predicate, not without a fairly high level reasoning (filter needs a Predicate; should the compiler perform an exhaustive search of the visible functional interfaces to see which one has a method with signature “Predicate negate()”?). So, yeah, it seems like in this case the programmer needs to communicate the intent.
>>
>> I think inference would be *possible*, but it’d be expensive, and would still allow for either ambiguities or accidentally matching something unexpected, so I think it would also be undesirable.
>>
> Ok.

I agree with Atilla, basically, the compiler can only infer the function 
type corresponding to a lambda so the compiler need to bridge the gap 
between the function type and the corresponding interface by using some 
vodoo incantation based on the types currently imported (at least).

>
>
>> I don’t have an opinion of whether we want an “of” static method on Predicate, as then it would have to indeed be introduced on many other interfaces. It’s more appealing than a cast, certainly; especially since the cast apparently needs to specify the explicit type parameter too.
>>
> I guess it's too late to consider an implicitly declared method along the lines of what Remi suggests. FWIW i always found such methods on enum a little too magical.

about values() and valueOf(), the main issue for my students is that 
these methods have no javadoc.

>   And perhaps it's too confusing to consider an invocation mode where "this" can be an implicit first parameter.

The opposite of the C# extension method, write an instance method and 
you can use it as a static method,
funny until someone will see when debugging that a call to a static 
method can run a method deep in the hierarchy :(

>
>
>>> In some respects i wonder if the default methods on the functional interfaces are an attractive nuisance.

Function composition is an important concept and being able to specify 
an implementation of an operation on a function is exactly what a 
default method on a functional interface is, by example:

interface Logger {
   void log(String msg);
   default Logger filter(Predicate<String> filter) {
     return msg -> {
       if (filter.accept(msg) {
         log(msg);
       }
     };
   }
}

// a Logger
Logger logger = System.err::println;
// a Logger that logs only message that starts with 'W'
Logger wLogger = logger.filter(msg -> msg.startsWith("W"));

>> Meaning, if .negate(Predicate) were a static method on the Predicate class instead of a default method, then stream.filter(Predicate.negate(String::isEmpty)) would be possible? Yeah…
>>
> Yeah. We are now in the unfortunate position where to alleviate this problem we might require duplicate static and default methods. I have been sitting on the issue a bit and nearly closed it a few times :-)
>
> Paul.

Rémi




More information about the core-libs-dev mailing list