Function type naming conventions

Sam Pullara spullara at gmail.com
Wed Jan 23 13:11:35 PST 2013


Seems super reasonable to me.

Sam

On Jan 23, 2013, at 12:34 PM, Remi Forax <forax at univ-mlv.fr> wrote:

> On 01/23/2013 09:19 PM, Sam Pullara wrote:
>> Awesome, this is where I hoped we would end up on this.
>> 
>> Thanks,
>> Sam
> 
> I agree with Sam :)
> but I still think that
> 
> > Plus some derived convenience types:
> >
> >   UnaryOperator<T> extends Function<T,T>
> >   BinaryOperator<T> extends BiFunction<T,T,T>
> 
> UnaryOperator should be Operator and BinaryOperator should be BiOperator,
> it's just more regular.
> 
> Operator<T> extends Function<T,T>
> BiOperator<T> extends BiFunction<T,T,T>
> 
> Rémi
> 
>> 
>> On Jan 23, 2013, at 12:11 PM, Brian Goetz <brian.goetz at oracle.com <mailto:brian.goetz at oracle.com>> wrote:
>> 
>>> Returning to this....
>>> 
>>> Recall that the primary goal in choosing SAM names is user ergonomics.  Not only do we want to make things clear, but we want to assign the simple names to the most commonly used configurations. Hence "Function<T,U>" instead of "ObjectToObjectFunction".  A secondary goal is for the names to be mechanically derivable from a reasonably small set of rules.
>>> 
>>> To recap where we are:
>>> 
>>> Base types, each with their own natural arity:
>>> 
>>> Block<T>
>>> 	T -> void
>>> Function<T,R> 	T -> R
>>> Predicate<T> 	T -> boolean
>>> Supplier<T> 	() -> T
>>> 
>>> 
>>> Plus some derived convenience types:
>>> 
>>>  UnaryOperator<T> extends Function<T,T>
>>>  BinaryOperator<T> extends BiFunction<T,T,T>
>>> 
>>> Plus arity prefixes to modify the natural arity of these:
>>> 
>>> BiFunction<T,U,R>     // (T,U) -> R
>>> BiPredicate<T,U>       // (T,U) -> boolean
>>> 
>>> We have a primitive specialization convention that lets you prefix {Int,Long,Double,Boolean,Obj} on front of one of these (possibly multiple times).  In accordance with our ergonomics goal, we want simple specializations for those that specalize return type, since that has been the most common specialization.  (Obj is used when you want to specialize later type params.)
>>> 
>>> We first tried ordering the type parameters return-type-first, so that partial specialization could proceed left-to-right.  This went over like a lead balloon.  So we then adopted the rule that we specialize return type first when the return type is generic, and otherwise left-to-right.  This means that
>>> 
>>>  IntFunction<T>
>>> 
>>> is a function T -> int.  This seemed attractive as T -> int was far, far more common than int -> T.
>>> 
>>> But this special treatment introduces anomalies.  For example, this rule would (probably) assign
>>> 
>>>  IntDoubleFunction
>>> 
>>> for double -> int, whereas users will probably expect IntDouble function to correspond to int -> double based on left-to-right.  There are ways to further complicate the mapping to avoid this but that just moves the anomalies into darker corners.
>>> 
>>> Based on offline discussions with Kevin and Joe, we concluded that backing off slightly from our desire to have the shortest possible name for the most common specialization can yield a more consistent naming scheme while still being acceptably ergonomic.
>>> 
>>> So, new proposal:
>>> 
>>> - When specializing return type, prefix "ToXxx"
>>> - Otherwise specializations gobble type arguments left-to-right.
>>> 
>>> So:
>>> 
>>>  Function<T,U>             // T -> U
>>>  DoubleFunction<T>    // double -> T
>>>  ToIntFunction<T>        // T -> int
>>>  DoubleToIntFunction  // double -> int
>>> 
>>> This also means there is more than one way to represent the same thing in some cases.  For example, also consistent with these rules are:
>>>  DoubleIntFunction    // double -> int
>>> since this covers all the type arguments.
>>> 
>>> Only Function and Supplier are generic in return, so specializations of Block and Predicate would not be affected by the new "To" rule.
>>> 
>>> This scheme is simpler and has fewer anomalies.  The casualty is that IntFunction becomes ToIntFunction -- arguably clearer, and only slightly more verbose -- overall seems a win.
>>> 
>>> Here's how the current SAMs would be affected:
>>> 
>>> IntFunction<T>
>>> 	ToIntFunction<T>
>>> IntBiFunction<T,U>
>>> 	ToIntBiFunction<T,U>
>>> IntBlock
>>> 	not affected
>>> IntBinaryOperator
>>> 	not affected
>>> IntPredicate
>>> 	not affected
>>> IntSupplier
>>> 	ToIntSupplier
>>> alternately, not affected
>>> IntUnaryOperator
>>> 	not affected
>>> ObjIntBiBlock
>>> 	not affected
>>> ObjIntFunction
>>> 	IntToObjFunction
>>> alternately, IntObjFunction
>>> 
>>> 
>>> A possible (orthogonal) tweak to this system would be:
>>> - When there are enough specializations to cover all arguments, omit the arity prefix
>>> 
>>> This would turn ObjIntBiBlock into ObjIntBlock.  Some more investigation would be required to ensure there are no collisions here.
>> 
> 



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