I think this would be a useful addition to the standard library. In fact, I started sketching out a pitch before I found this post!
I've included my pitch below which includes more details about how it satisfies the criteria mentioned in the toggle
proposal.
Let me know what you think @SiliconUnicorn and others. Maybe we can team up and see if we can get some momentum behind this.
Introduction
I propose adding var isEven: Bool
and var isOdd: Bool
to BinaryInteger
. These are convenience properties for querying the parity of the integer.
- Swift forums thread: Even and Odd Integers - Pitches - Swift Forums
Motivation
It is sometimes useful to know the evenness or oddness (parity) of an integer and switch the behaviour based on the result. The most typical way to do this is using value % 2 == 0
to determine if value
is even, or value % 2 != 0
to determine if value
is odd.
// Gray background for even rows, white for odd.
view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white
// Enable button if we have odd number of photos
buttonSave.isEnabled = photos.count % 2 != 0
It is also possible to use the bitwise AND operator (value & 1 == 0
) which will inevitably lead to discussions about which one is faster and attempts at profiling them, etc, etc.
There are a few more specific motivations for this proposal:
Commonality
The need to determine the parity of an integer isn’t restricted to a limited problem domain.
Readability
This proposal significantly improves readability. There is no need to understand operator precedence rules (%
has higher precedence than ==
) which are non-obvious.
The properties are also fewer characters wide than the modulus approach (maximum 7 characters for .isEven
vs 9 for % 2 == 0
) which saves horizontal space while being clearer in intent.
view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white
view.backgroundColor = indexPath.row.isEven ? .gray : .white
buttonSave.isEnabled = photos.count % 2 != 0
buttonSave.isEnabled = photos.count.isOdd
Discoverability
Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions:
c - How do I check if an integer is even or odd? 300,000+ views
java - Check whether number is even or odd 350,000+ views
Check if a number is odd or even in python 140,000+ views
IDEs will be able to suggest .isEven
and .isOdd
as part of autocomplete which will aid discoverability.
Consistency
It would be relatively easy to reproduce the properties in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (Int
?, SignedInteger
?, FixedWidthInteger
?, BinaryInteger
?). This inconsistency can be seen in a popular Swift utility library which defines these properties on SignedInteger
which results in the properties being inaccessible for unsigned integers.
These properties will also eliminate the need to use modulus 2 and bitwise AND 1 to determine parity.
Adding isEven
and isOdd
is also consistent with the .isEmpty
utility property, which is a convenience for .count == 0
.
if array.count == 0 { ... }
if array.isEmpty { ... }
if value % 2 == 0 { ... }
if value.isEven { ... }
Correctness
There is a minor correctness risk in misinterpreting something like value % 2 == 0
, particularly when used in a more complex statement, when compared to value.isEven
.
Performance
This proposal likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to @inlineable
.
Proposed solution
Add two computed properties, isEven
and isOdd
, to BinaryInteger
extension BinaryInteger {
@inlinable
/// A Boolean value indicating whether this value is even.
///
/// An integer is even if it is evenly divisible by two.
public var isEven: Bool {
return self % 2 == 0
}
@inlinable
/// A Boolean value indicating whether this value is odd.
///
/// An integer is odd if it is not evenly divisible by two.
public var isOdd: Bool {
return self % 2 != 0
}
}
Similar functionality exists on Ruby’s Integer type: even? and odd?.