Division-related Numeric extensions?

The Numeric protocol only specifies addition, subtraction, and multiplication. It doesn't specify division. Besides some ADTs possibly not having division, there are two distinct kinds: multiplicative inverse, and quotient & remainder. Floating-point types use the former, while integer types use the latter. Like SignedNumeric extends Numeric with additive inverses, we could do something similar for division.

DivisibleNumeric: extends Numeric with / and /=.
RefinedDivisibleNumeric: extends DivisibleNumeric with reciprocal() and reciprocate().
ChunkedDivisibleNumeric: extends DivisibleNumeric with %, %=, and quotientAndRemainder(dividingBy:).

I just thought about zero divisors, as in making instance-level properties for this state. But it only applies to rings, and I don't know whether we insist that a conforming type is a ring first. (And how, with another protocol ("RingNumeric")?) A zero-divisor can be left or right, with those being the same for commutative rings. (Yet another protocol for commutativity?) And would all of this be worth it if types with non-trivial zero divisors are rare?

1 Like

What would the return type of /? That is, we can have:

func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self

but DivisibleNumeric is not closed under division, so how would we represent it?

Do you mean DivisibleNumeric? Because Numeric isn’t changing. But the operator would return Self, like the others. (/= wouldn’t work otherwise.)

I don’t know if it’s enforceable, but types should choose one of DivisibleNumeric’s sub-protocols.

Oops, yeah, that's what I meant.

The problem with putting division into a protocol like this is what semantics it should have. A protocol requirement embodies not merely syntax, but *meaning*, and that meaning is what allows generic algorithms to be written against protocols.

If we added a Field protocol, then a hypothetical Rational type could conform, but FloatingPoint could not because of representation issues. In particular, Field wants to define division as the inverse of multiplication, so that “(a/b) * b == a” is true (for nonzero b), but floating point types cannot meet that guarantee.

So, maybe an ImperfectlyRefinedDivisibleNumeric?

A division protocol wouldn't affect the ability of different types to divide. Doesn't a protocol only specify that it can be done, not that it can be done excellently or this way or that? A protocol shouldn't worry about the implementation.

For example, I can create a type that conforms to Equatable and provides an implementation that doesn't check Equatable at all. I don't think anyone expects protocols to enforce the implementation, only the signatures, the input type/s and output type/s.

On the contrary, an oft-repeated Swift design principle is that protocols should represent semantics, not just operations. The compiler can’t enforce semantic corrections, but as human beings we can accurately say that an Equatable conformance that doesn’t fulfil the semantic requirements is wrong.

5 Likes