Explicit Integer Division Operator

In Swift currently, the / operator is used to handle floating point and integer division, depending on the types of the operands. This seems rather natural but can lead to bugs (example below). In some other languages, such as python, there are separate operators for integer division and floating point division. This would allow for compile time errors and more readable code.

For the sake of this argument I'm going to copy python's operators:
/ for floating point division
// for integer division

Imagine you have a class:

class View {
  let animationDuration: Float = 0.3 // seconds

  func animate() {
    UIView.animate(withDuration: TimeInterval(animationDuration), ...)
  }
}

Later, a someone decides that this animationDuration should be in milliseconds, and notices that it can be an Int instead of a Float.

class View {
  let animationDuration: Int = 300 // milliseconds

  func animate() {
    UIView.animate(withDuration: TimeInterval(animationDuration/1000), ...)
  }
}

In this example TimeInterval is an alias for a Double.

Do you see the bug? It should be written as TimeInterval(animationDuration)/1000 as it is first doing integer division (in this case the result would be 0) and then converting to a TimeInterval. It is a silly mistake but it is one that is easy to make and could be improved by using separate operators. If this were using my proposal, both would use floating point division. I find that in applications floating point division is much more common than integer division.

In other cases where you do want integer division, you could write something like this:

class Foo {
  let a: Int
  let b: Int

  func doSomething() {
    ...
    let c = a // b
    ...
  }
}

Which gives the reader a better picture of your intentions in a big class, and they don't have to look at the types of a and b to know what you intended. Later if someone decides that b should be a floating point number, they would get a compile time error as they tried to do integer division with a floating point number.

We could add //, but we wouldn't be able to take away / on integers, because that's a source-breaking change (it's also a binary-breaking change, but let's focus on the source aspects). Which means that you wouldn't get the compile-time error.

Actually we can't add // b because that is a comment.
It would have to use other symbols.

6 Likes

My idea of it isn't to remove the / operator for integers necessarily but to make it so that the return type of / is always a floating point type and // is always an integer type. Although it would still be source-breaking, there is a potential conversion path I think. As if a division is being performed, if the expected return type is a Double then it can remain the same in the source code, and if the expected result is an Int it could be replaced by //.

So say some code has been written in current Swift:

let foo = 1
let bar = 3
let foobar = foo / bar
print(addOne(foobar))

func addOne(_ a: Int) -> Int {
  return a + 1
}

In this case some conversion script could potentially recognize that the expected type is an Integer and convert / into //. Although there would probably be some difficult cases to infer, I admit that.

That doesn’t fit at all with how Swift’s type system currently handles arithmetic, and the current system is great at preventing this kind of error in practice anyway.

In Swift, the result of an arithmetic operation always has the same type of both operands, so an Int divided by another is always an Int; and a Float divides by another is always a Float. The lack of implicit conversions ensures that you can’t accidentally use integer division when you meant to use floating-point, since the compiler won’t let you use an Int somewhere where a Float is expected.

In your example:

TimeInterval(animationDuration/1000)

The integer division error is already obvious because of the type conversion. The type conversion isn’t boilerplate, it’s a reminder that you’re converting an integer into a floating-point value.

2 Likes

Changing the return type is a much tougher sell than removing the existing API and adding a new operator. If you remove the API (although not the ABI), all users will get a compile error that they can fix. Changing the return type may not cause compile failures in all cases, so you can end up with silent behaviour changes. Even if we could theoretically transform all uses in a migrator tool, there is no guarantee that users will run that migrator. The bar for this kind of change would be high.

1 Like

Also there is no floating-point type in the standard library which can losslessly express the result of dividing two 64-bit integers.

2 Likes