Review request for initial lambda functions and utils

Rémi Forax forax at univ-mlv.fr
Wed Aug 10 06:22:38 PDT 2011


On 08/10/2011 12:54 AM, Kevin Bourrillion wrote:
> On Tue, Aug 9, 2011 at 3:32 PM, Rémi Forax <forax at univ-mlv.fr 
> <mailto:forax at univ-mlv.fr>> wrote:
>
>     Let's take an example, Predicates.compose is declared like this:
>     <A,B> Predicate<A> compose(Predicate<B> predicate, Function<A, ?
>     extends B> function)
>
>     Basically, your signature works in most of the cases because
>     you rely on the compiler to infer of A and B  but there are some
>     known cases
>     by example if compose is called as a parameter of another method call
>     where the inference is not done by the compiler.
>
>
> What we've tried to do is ensure that A and B can be inferred directly 
> from the parameters given without having to rely on the method return 
> value being directly assigned to a variable from which the type 
> information could be gleaned.
>
>     If the compiler don't do any inference, the easy workaround
>     is to provide a type argument for A and for B,
>     but with your API, it will not work.
>
>     void foo(Predicate<String> p) { ... }
>
>
> You've lost me right there.  That needs to be Predicate<? super String>.
>
>     ...
>     Predicate<String> p = ...
>     Function<Object, String> f = ...
>     foo(Predicates.compose(p, f))    // doesn't compile, Ok cf Java spec
>
>
> Just fix foo() and this works.  No explicit type parameters needed.

Right, if you change the test case, the bug disappear !
More seriously, I don't control foo().

>
> It would be improper for compose(p, f) to return a Predicate<String> 
> because a Predicate<Object> is /what it is/.  It's a predicate that's 
> capable of handling any object.  There are very rare circumstances 
> (well, rare when APIs are designed correctly) where you are forced to 
> "pretend" that a Predicate is less capable than it really is. 
>  Cast-and-suppress is a perfectly reasonable escape valve for those 
> situations.

I can't disagree more, it's a common pattern when you want to share lambdas.
Example, I want to share/reuse a projection function (your Function) to 
take different types
knowing I will call toString() or hashCode() on the parameter of the 
projection function,
I will define the function once taking an Object even if the resulting 
Predicate
returns different types.
<T> Predicate<T> toStringCompose(Predicate<T> predicate) {
       return Predicates.compose(predicate, TO_STRING_FUNCTION);
     }

Another example, I have two classes A and B with B that inherits from A,
A defines a field (or a property). The projection function is defined to 
take an A
and returns the value of the field. I can create a predicate on a A or 
on a B,
I will not write the projection function twice, I will reuse the same 
function
and compose to create a Predicate of A or B.

So it's perhaps you haven't seen these use cases because there is no 
method reference
in Java but as you know it will change.

And I don't want to cast (and suppress) something i.e. add noise to my 
program
just because you think everybody should not call your API by providing 
the type
argument explicitly.

>
>
>     foo(Predicates.<String,String>compose(p, f))    // doesn't compile
>     too ??
>
>
>>     but I can easily say that your compiler sounds like anything but
>>     a /typical/ user project, which JDK APIs should be tailored to.
>
>     An API should work :)
>
>     Rémi
>

Rémi



More information about the lambda-dev mailing list