Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a "function shape" with respect to functional interfaces in Java 8?

In Java 8 the new package java.util.function contains a lot of functional interfaces. The documentation for that package (http://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html) makes several references to "function shapes":

  • There are several basic function shapes, including Function (unary function from T to R), Consumer (unary function from T to void), Predicate (unary function from T to boolean), and Supplier (nilary function to R).
  • Function shapes have a natural arity based on how they are most commonly used. The basic shapes can be modified by an arity prefix to indicate a different arity, such as BiFunction (binary function from T and U to R).
  • There are additional derived function shapes which extend the basic function shapes, including UnaryOperator (extends Function) and BinaryOperator (extends BiFunction).

I had never heard of the term "function shape" before, and I can barely find a reference to it anywhere except in the documentation above, but since that is Oracle's formal documentation on functional interfaces I'd like to understand it.

Can anyone provide a definition of "function shape", and invent an example? Is it a general term in Computer Science, or does it relate only to Java 8? And how is function shape related to a function descriptor (such as (T) -> boolean for the Predicate<T> interface)?

UPDATE The two comments below from Brian Goetz answer the questions I raised in this post.

like image 233
skomisa Avatar asked Sep 30 '22 16:09

skomisa


2 Answers

A function shape is basically what its inputs and outputs look like, in terms of the type parameters:

  • A unary function takes one input and returns one output [T→R]
  • A binary function takes two inputs and returns one output [(T,U)→R]
  • A ternary function takes three inputs and returns one output [(T,U,V)→R]
  • A supplier (also known as a nullary function) takes no input and returns one output [()→R]
  • A consumer takes one input and doesn't return any output [T→()]
  • A unary predicate takes one input and returns one output of boolean type [T→bool]
  • A binary predicate takes two inputs and returns one output of boolean type [(T,U)→bool]
  • A unary operator takes one input and returns one output of the same type [T→T]
  • A binary operator takes two inputs of the same type and returns one output of the same type [(T,T)→T]

There are many other shapes, but those are common ones.

like image 96
Chris Jester-Young Avatar answered Dec 31 '22 20:12

Chris Jester-Young


I didn't find any reference to an official or widely accepted definition of the term "function shape", so what follows is my own interpretation.

A "function shape" appears to be its "type signature" including the return type, i.e. the sum description of:

  • an ordered list / tuple of the types of its parameters, and
  • its return type

(That is, basically everything about a function except function name, parameter names, and body.) I suspect that they didn't use the term "signature" because it already has a different meaning in Java — it doesn't include the return type. So they invented a new term.

In functional programming languages, "type signature" usually includes the return type. Signatures are helpful in understanding what a function might do, so they are often written down explicitly. For example, the signature (or "function shape" in Java's terms) for the new BiFunction might be written down as (T, U) -> R, where the first part is a tuple representing the parameter list, and the second part is the return type.

I therefore disagree with this other answer: I think that the types matter and are not foregone. If they were foregone, then several types defined in that new namespace would have exactly the same function shape (e.g. Supplier, Predicate, Function). If that were so, then why would the documentation choose to explain these new types with the mismatching concept of function shapes? That doesn't make sense. (The answer has since been edited.)

Here are a couple more examples of functional type signatures for the new Java functional interfaces:

BiFunction<T,U,R>,            (T, U) -> R
BinaryOperator<T,U,R>         (T, U) -> R
BiPredicate<T,U>              (T, U) -> boolean
Consumer<T>                   T      -> ()            note: `()` means `void`
Function<T,R>                 T      -> R
IntFunction<R>                int    -> R
Predicate<T>                  T      -> boolean
Supplier<R>                   ()     -> R
UnaryOperator<T,R>            T      -> R 
like image 42
stakx - no longer contributing Avatar answered Dec 31 '22 20:12

stakx - no longer contributing