[swift-evolution] Failable arithmetic
Brent Royal-Gordon
brent at architechies.com
Fri Dec 4 16:40:10 CST 2015
Currently, Swift has three ways to handle potential overflows and other errors in arithmetic:
// 1: Crashes
Int.max + 1
// 2: Returns the wrong answer (Int.min in this case)
Int.max &+ 1
// 3: Returns a tuple with the value of &+ and a boolean indicating whether it overflowed
Int.addWithOverflow(Int.max, 1)
The problem is, if you want to handle overflows in some simple way, none of these are very good. 1 terminates your app, 2 gives the wrong answer, and 3 is very awkward to use. If you’re, for instance, working with numbers input by the user or downloaded from the Internet, you don’t want 1 or 2, and 3 is a major pain. You’re not looking to figure out exactly what went wrong; you just want to show the user “Arithmetic error” or something, rather than crashing or giving a wildly incorrect answer.
Therefore, I propose that we add failable arithmetic operators (e.g. +?). These return nil on overflow and the result on non-overflow.
1 +? 1 // => Optional(1)
Int.max +? 1 // => nil
1 -? 1 // => Optional(0)
Int.min -? 1 // => nil
1 /? 1 // => Optional(1)
1 /? 0 // => nil
One important consideration is that you ought to be able to chain such operations together into expressions like "m *? x +? c”. The simplest way to do this would be to make these operators take optional arguments; then the implementations would all look something like this:
func +? <Integer: IntegerArithmeticType>(lhs: Integer?, rhs: Integer?) -> Integer? {
guard let lhs = lhs, rhs = rhs else {
return nil
}
let (result, overflowed) = Integer.addWithOverflow(lhs, rhs)
if overflowed {
return nil
}
else {
return result
}
}
However, that might encourage people to misuse these operators to simply perform arithmetic on optional integers even when they don’t want nil-on-overflow. There may be some clever way to get these operators to allow chaining from other failable operators, but prevent the use of other nil-returning expressions; I’m not sure how that would be done, but I wouldn’t mind if Swift forced that sort of hygiene on this feature.
An alternative approach might be to write throwing variants of these operators which require the use of “try". These would have a few advantages: they would naturally short-circuit, they wouldn’t form an attractive nuisance for people trying to do arithmetic with optional integers, and the errors they throw could provide additional detail. However, unlike the association of Optional with ?, there’s no obvious way to associate these operators with the idea of trying and throwing. For this example, I’ve used “+!” purely for lack of a better idea:
do {
print(try 1 +! 2 +! Int.max)
}
catch let error as IntegerArithmeticError { // or perhaps IntegerArithmeticError<Int>
print(error) // AdditionOverflow, or perhaps even AdditionOverflow(3, 9223372036854775807)
}
Of course, if you really did just want an optional, you could always use “try?” with this.
--
Brent Royal-Gordon
Architechies
More information about the swift-evolution
mailing list