Function type naming conventions

Stephen Colebourne scolebourne at joda.org
Wed Jan 2 16:23:09 PST 2013


My natural reaction for IntFunction is to expect (int) -> T

As such, I thought about my mental model, what the "base word" in each
means - Block/Function/Predicate/Supplier - and how the primitive
interacts with it. My conclusion was that the prefix should refer to
the most important element of the base, the one you think about most
when using it (for functions thats the input, not the output). So, I'd
like to propose the following naming (only slight changes from today):

If the base interface naturally has a single generic, then the
primitive is prefixed:

  Block<T>               T -> void
  IntBlock               int -> void

  Supplier<T>         () -> T
  IntSupplier            () -> int

  Predicate<T,U>       (T) -> boolean
  IntPredicate<U>       (int) -> boolean

  UnaryOperator<T>      (T) -> T
  IntUnaryOperator      (int) -> int

  BinaryOperator<T>      (T,T) -> T
  IntBinaryOperator      (int,int) -> int

  BiPredicate<T,U>       (T,U) -> boolean
  IntBiPredicate<U>       (int,U) -> boolean
  IntDoubleBiPredicate    (int,double) -> boolean

This really only leaves Function. I came to the conclusion that my
preference was to find an alternative base name for functions that
return Int/Long/Double (in the same way that Predicate is a function
that returns boolean). I came up with these naming strategies:

  CalcInt, CalcLong, CalcDouble
  FuncInt, FuncLong, FuncDouble
(I wanted a properly unique name for each, like predicate, but
couldn't find one, Calc is short for Calculator, and I think its my
preference as it emphasises the difference from Function, like
Predicate does)

Thus, we now have a unique base name for each return type, so the
remaining generics to be populated are all prefixes:

  IntFunction<T>         int -> T
  CalcInt<T>             T -> int

  BiFunction<T,U,R>     (T,U) -> R
  IntBiFunction<U,R>     (int,U) -> R
  IntDoubleBiFunction<R>     (int,double) -> R
  BiCalcInt<T,U>        (T,U) -> int
  IntDoubleBiCalcInt       (int,double) -> int

I believe the overall naming rule is now, "populate the generics with
the prefixed primitives in order".

FWIW, I dislike Indexed as it is use case sensitive. I also think
BiOperator would be a much more obvious name than BinaryOperator.

Stephen


On 2 January 2013 19:23, Brian Goetz <brian.goetz at oracle.com> wrote:
> Just to recap where we are in function type naming, we've got a few base
> types:
>
>   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>
>
> We have arity prefixes to modify the natural arity of these:
>
>   BiFunction<T,U,R>      (T,U) -> R
>   BiPredicate<T,U>       (T,U) -> boolean
>
> Presumably we'll be forced into TriXxx and worse at some point, but so far
> we've been able to avoid that.
>
> We have a primitive specialization convention that lets you prefix
> {Int,Long,Double,Boolean} on front of one of these (possibly multiple
> times), and this gobbles the type argument that appears in the return
> position, or, if the return is not generic, the first type argument:
>
>   IntFunction<T>         T -> int
>   IntSupplier            () -> int
>   IntBiFunction<T,U>     (T,U) -> int
>   IntBinaryOperator      (int,int) -> int
>   IntBlock               int -> void
>
> So far we've got about 30 defined SAMs, and this has been mostly enough to
> implement our libraries, including primitive specializations.
>
> This convention is an uneasy compromise that is part and parcel of the
> compromise/commitment we made to nominal function types.  Its a bit annoying
> in places, but the rules above are consistent, and the names read mostly
> reasonably in APIs, and better than most of the alternatives proposed,
> especially the fully general Function$FromIntAndDouble$ToDouble.
>
> In fact, the only type we've come across repeatedly that wants a "nice" name
> that doesn't fit into the above scheme is:
>
>   int -> T
>
> which shows up in quite a few places.  There are probably a few others but
> this is the biggest.  (We also came across a desire for (T,int)->T in the
> reducer work, but that shows up in only one place.)
>
> We could treat int -> R as a specialization of Function<T,R>, and extend the
> naming convention to handle it, or we could try to come up with a new
> top-level name for it.
>
> In the latter category, IndexedSupplier<T> would work for some of the use
> cases (Arrays.fill, array suppliers) but not terribly well for
> IntStream.map(int -> T).
>
> Other ideas?


More information about the lambda-spec-comments mailing list