Reciprocal operator

Hello, Swift community!

This is a very small pitch to add a new operator definition and implementation to the standard library:

prefix operator /

Its primary use case is to mean 1 / something. This would make swift operators more coherent, since the reciprocal is the multiplicative inversion operation, while the additive inversion operation is already implemented as the prefix minus. Also, it could be used to provide a default implementation of protocol requirements for division operations in terms of multiplying the lhs by the reciprocal of the rhs, which would be useful for things like numeric wrapper types.

Another use case that I had in mind is path is using it to construct path expressions like /"foo"/bar/"baz.swift".

I haven’t seen that operator in any other language that I know of, and usually Swift only defines operators that are very common. An operator is like a term of art: there’s always a longer, more descriptive synonym for it but we still choose to use the more common one (Double.sin instead of something like Double.yValueOnGeometricCircle(angle:), + instead of Int.added(to:), etc.). The -x prefix operator is an universal mathematical shorthand for 0 - x whereas 1/x is always written as either 1/x or x^-1.

As for the path construction operator, that’s a different use of / altogether. Several libraries define it and I define it too sometimes myself, but it doesn’t fit in the standard library. The standard library barely has anything for input and output, let alone filesystem paths or URLs. It would be a good candidate operator for Foundation or any other library that vends filesystem API or for a library that implements URL routers like Vapor. :slight_smile:

5 Likes

Not sure / someNumber really looks any more clear than 1 / someNumber, and looks worse a lot of the time, e.g let z = x * / y or confusing like let z = / x * y. Also as @ctxppc mentions it's not really consistent with mathematical notation.

5 Likes

Swift Numerics has:

/// The (approximate) reciprocal (multiplicative inverse) of this number, if it is representable.
///
/// If reciprocal is non-nil, you can replace division by self with multiplication by reciprocal and
/// either get exact the same result (for finite fields) or approximately the same result up to a
/// typical rounding error (for floating-point formats).
///
/// If self is zero, or if a reciprocal would overflow or underflow such that it cannot be accurately
/// represented, the result is nil. Implementations should be *conservative*; it is OK to return
/// nil even in some cases where a reciprocal can be represented. For this reason, a default
/// implementation that simply always returns nil is provided.
///
/// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil for `Real` or
/// `Complex` types, because these types have an `.infinity` value that acts as the
/// reciprocal of `.zero`.
var reciprocal: Self? { get }

Making this an optional property makes it more useful in writing algorithms, because you can fallback on something else when the reciprocal isn't representable:

func divide<T: Real>(data: [Complex<T>], by divisor: Complex<T>) -> [Complex<T>] {
  // If divisor is well-scaled, use multiplication by reciprocal.
  if let recip = divisor.reciprocal {
    return data.map { $0 * recip }
  }
  // Fallback on using division.
  return data.map { $0 / divisor }
}

I don't see a significant need to have an operator for it specifically. What use cases do you have in mind where you feel like that would be beneficial?

9 Likes

Honestly, this was mostly for consistency’s sake. We have an additive inverse operator, which can be used to implement a generic subtraction operation based on it and on addition, while the same thing isn’t available for multiplicative operations. And yes, I know that for unsigned integers that default implementation of subtraction would be incorrect.

To introduce the / as a potential prefix operator available for overloading could be valuable, and could be supported if there was a consensus around it. I don't feel strongly about supporting it though.

I don't think it is a good idea to automatically introduce it as a prefix arithmetic operator to express fractions, it is not a familiar or conventional notation.
I think the consistency in case of the - operator as an inverse additive operator comes from regular arithmetics, where we do have 4 (or +4) and the inverse -4. These values together with the operators represent a value or amount of something, as well as an operation to be performed with respect to 0.

I haven't really seen the multiplication or division operator used in the same way in maths or other coding languages, e.g. *5 or /5. Maybe because this is an instruction to perform an operation but does not represent a standalone value usually.

One could argue /5 to be used to represent 1/5, but then something like this example

     let x = 10 * /5  // x is now 2

though syntactically correct with this suggestion, seems out of place.
Side note for this example, do I understand correctly that result above would be a double 2.0? Even though the result of the original operation (10/5 = 2) is a perfectly valid integer.

1 Like

One consideration is that, for reasons that are still not totally clear to me, prefix - is one of the worst cases for typechecker performance in the standard library; I expect that a prefix / would incur similar issues. We shouldn't be designing the standard library around typechecker limitations, but neither should we go out of our way to cause more issues =)

3 Likes

Due to potential intermediate rounding errors, i think so. The reciprocal operator wouldn’t be defined on integer types, because integer types, by definition, can never represent their own reciprocals.

Best practice for constructing paths is to use URL(). Given that / isn't the universal path separator this probably also wouldn't be advised (see Windows.).