Background
Currently, Swift accepts 0 or more arguments for a variadic parameter. This makes sense for some functions, but not necessarily for functions that have a label on the variadic parameter.
TensorFlow has a method called sum(alongAxes:)
. This method performs a reduction on Self
along specified axes. It's defined as follows:
public extension Tensor where Scalar : Numeric {
/// Returns the arithmetic mean along the specified axes. The reduced
/// dimensions are retained with value 1.
/// - Parameter axes: The dimensions to reduce.
/// - Precondition: Each value in `axes` must be in the range `-rank..<rank`.
func sum(alongAxes axes: Int...) -> Tensor {
...
}
}
We expect this function to be called with 1 or more arguments, for example:
let x: Tensor<Float> = [[[1, 2]], [[3, 4]]]
x.sum(alongAxes: 2) // [[[3.0]], [[7.0]]], the sum along axis 2
x.sum(alongAxes: 0, 1) // [[[4.0, 6.0]]], the sum along axes 0 and 1
However, when this functions called with no arguments, the result becomes completely confusing.
x.sum() // [[[1, 2]], [[3, 4]]]. Reduced along **no** axis!
This is unexpected to the user, because the call site x.sum()
without argument label alongAxes:
directly implies that this is a summation of all elements. Currently there's no way to make this method reject 0 arguments!
To resolve this, we had an overloaded method in the TensorFlow library:
func sum() -> Scalar {
// Reduce along all axes (all elements).
// Reshape to a scalar.
// Return the scalar.
}
In most cases when users call x.sum()
, it refers to the no-argument sum()
. However, when there's a contextual type Tensor
, calling x.sum()
would still refer to sum(alongAxes:)
and make it a no-op!
Note: While making the no-argument sum()
return a Tensor<Scalar>
instead of Scalar
can completely shadow sum(alongAxes:)
at call sites where there's no arguments, we don't want to do that because the result shape of "sum of all elements" is guaranteed to be a scalar. There can be a more systematic solution than requiring library designers to overload and shadow things.
Possible Solutions
1. Reject zero arguments when the parameter has a label
func mean(alongAxes: Int...) -> Tensor { ... }
x.mean()
~~~~^ Variadic parameters with an argument label requires at least 1 argument
2. Introduce a parameter attribute to specify one-or-more arity
func foo(alongAxes: Int...) -> Tensor { ... }
func bar(alongAxes: @oneOrMore Int...) -> Tensor { ... }
x.foo() // ok, same as the current behavior
x.bar()
~~~~^ Variadic parameter requires one or more arguments