apply

Brian Goetz brian.goetz at oracle.com
Sun Dec 16 18:07:43 PST 2012


OK, let's try to get this all in one place.  Mostly a defense of the 
status quo but there's some possibly good news at the end for apply fans.

1.  Functional interfaces are special interfaces, but they are 
interfaces.  They can be implemented with lambdas, but they can also be 
implemented by anonymous classes or implemented by classes with other 
methods.  Choosing sensible method names is a key part of API design; 
there's a reason why we called the method in Runnable "run" and not 
"apply".  Run is a more descriptive name.  One could design a library 
where all methods are named "Harold", but it would be hard to write and 
hard to use.  And there are likely to be functional interfaces where 
"apply" is an outright terrible name for what the thing does, and the 
"all same" rule will feel pretty constraining.

2.  Choosing nominal function types was a compromise -- a big one. 
Structural function types are better than nominal ones in almost every 
way.  (Reminder: we went this way because erased and always-boxed 
structural function types would *really* suck.)  One of the few 
advantages of nominal function types is that they have a name.  Naming 
them all Harold seems like deciding that since we chose a route with 
disadvantages, we don't deserve any of the few remaining advantages.

3.  Naming all SAM methods Harold slams the door on implementing many 
combinations of SAMs in a single class, or having certain SAMs extend 
each other.  Again, in a pure functional world, the notion of 
implementing multiple function types doesn't make much sense.  But, 
functional interfaces are interfaces, and objects may very well want to 
implement multiple functional interfaces.  Naming them all Harold means 
that some combinations can be implemented this way, but some not, which 
is a half-here, half-there state of affairs.

An obvious example is:

   interface SupplierOfBoxedIntegers {
       Integer apply();
   }

   interface DispenserOfInts {
       int apply();
   }

(First of all, apply is a terrible name for this class.)  But more 
importantly, no class can implement both, even though it might be 
entirely reasonable for a class IntFlinger to implement all manner of 
"give me an integer" methods, to maximize compatibility with / minimize 
needed adaptation for multiple libraries that use a dispenser of 
integers.  If they are all called Harold, IntFlinger is out of luck.

In this example, the two functions that IntFlinger wants to implements 
are basically the same function modulo boxing.  But, sometimes you may 
want to implement mutiple SAMs because their semantics are coupled 
through the semantics of your class.  For example:

    class MapMembershipArbiter<K,V> {
       Map<K,V> m;

       implements Predicate<K>, Function<K,V> {
           boolean test(K k) { return m.containsKey(k); }
           V apply(K k) { return m.get(k);
    }

If both methods were named apply, you couldn't do this.

Now, you might say "that's a stupid example" (and you might be right!) 
But, the "all different" rule allows for the possibility that this might 
actually be reasonable in some configuration; the "all same" rule 
ensures that this can never happen.


That said, I am not unsympathetic (well, unless you don't use an IDE, in 
which case I'm completely unsympathetic.)  I do find myself tripping 
over UnaryOperator.operate vs Function.apply since they're both just so 
functiony.  And here's where there might be some good news for you. 
Since we currently have

   interface UnaryOperator<T> extends Function<T,T>

then it actually is quite reasonable for UnaryOperator to adopt the name 
from Function, since there is no way to implement UnaryOperator without 
implementing Function.  In which case some of the offenders -- 
Unary/BinaryOperator -- can go away.

Similarly, SAMs of different arities can safely use the same name.  So 
Function and BiFunction both use the same name.






On 12/16/2012 5:10 PM, Doug Lea wrote:
>
> (Still in the midst of the painful setup allowing
> JDK8 j.u.c builds to track lambda-libs...)
>
> Could someone please explain clearly and compellingly why we are using
> different method names for all the functional forms in java.util.function
> instead of just "apply"?
> Does anyone think that other users won't find this very annoying?
>
> -Doug


More information about the lambda-libs-spec-observers mailing list