Even and Odd Integers

The problem with that is that you first have to figure out who that "general human" is and what they actually understand. It might not be what either one of us two understand.

That said, I don't think it's too much to ask of people that they should learn what a method they want to use does. It's not like we rename "map" because people might think it has something to do with Apple Maps.

(Also "the intricacies of number theory" != "literally the first thing about Number Theory". Should we replace the type "Int" with "WholeNumberWithLimitedRange", too?)

5 Likes

I understand where you're coming from but I think "divisible" is generally considered to mean "able to be divided without a remainder".

Oxford definition for divisible (mathematics): (of a number) containing another number a number of times without a remainder. ‘24 is divisible by 4’

Wolfram also has a divisible function. "Divisible[n,m] is effectively equivalent to Mod[n,m]==0."

16 Likes

Type “is divisible by” (with quotes) into your favorite search engine. When I did, I found many high-ranked hits on pages clearly intended for a general audience, even a grade-school audience, not mathematicians. All the pages I checked clearly define “is divisible by” as equivalent to “leaves zero remainder when divided by”. It is unfair to say that definition demands the reader be “well-versed in number theory in order to understand”.

I'm not saying “is evenly divisible by” isn't also common usage. It is. But I personally don't like that phrasing, because it's got “even” embedded in it, which also means “divisible by two”, so why muddy the waters? If you want to be pedantic, “is exactly divisible by” avoids overloaded terms.

But I think “is divisible by” is generally understood to mean “leaves zero remainder when divided by”, and will be understood that way by most Swift users, regardless of mathematical background.

15 Likes

Iff we really wanted a different name to avoid possible ambiguity that I don't completely agree is there then I would suggest hasFactor(_:) (we shouldn't have to pronounce Integer, at least, because types)

At some point this reduction reaches absurdity. We have to assume some mathematical preliminaries. Can we assume users know what "+" means, or do we need to spell that out too? Swift integers don't add like normal integers; should it be x.addingOrPossiblyTrapping(y) instead? "is divisible by" is a totally standard phrase in semi-technical English, and easily searched for if anyone is confused.

4 Likes

As a non-native English speaker I immediately understand isDivisible(by:), but when I would come across isEvenlyDivisible(by:), I'd probably google for 'evenly divisible' to find out what that means.

2 Likes

I've never heard anyone use "is divisible" that way. Every time I've ever heard anyone say that x "is divisible by" y, they meant that the remainder of dividing x by y is zero. Your definition of the phrase is so trivial as to be completely useless. The only thing that your definition of "is divisible" can distinguish between is zero and not zero, which we already have a simpler and more idiomatic category for: non-zero.

3 Likes

i got more confused by isEvenlyDivisible(by:) bc it sounds like it has to do with the number being even

2 Likes

While I understand what you are trying to say, I think that for the vast majority of Swift users, isDivisible(by:) has the exact same connotation as isEvenlyDivisible(by:).

If an Int value can be divided in a way that my high school math teachers would consider mathematically correct (eg. 3/3 = 1 or 10/2 = 5) than I think that nearly all Swift programmers would intrinsically understand that isDivisible(by:) would return true while it would otherwise return false, if only because it wouldn't make sense to have something that returns true in all circumstances except when the parameter is zero.

Yes, you can divide 10 by 3, but that question isn't the question most people care about or mean. The english language (and presumably many others) are filled with questions and statements that technically have one meaning, but are interpreted another way.

The meaningful question is whether or not it divides without a remainder, which is the implied meaning of the question among everyone I've interacted with on the topic.

2 Likes

I have resisted being pedantic for quite a while now, but to be 100% clear:
You can divide 10 by 3 in the domain of real numbers (or rational numbers), which we can approximate by floating point types. You can't divide 10 by 3 in the domain of integers, which is what the Int datatype tries to represent. Therefore there's no sloppy language anywhere.

We introduce Swift developers do different types anyway, so it makes sense that Int and Double would behave differently (for the record, it wouldn't be technically wrong to say that 10.0.isDivisible(by: 3.0) == true, although that's probably so useless and confusing that it's best not to define that method for non-integers). If anything, it's the fact that 10 / 3 = 3 in so many programming languages (but e.g. not in Haskell) which is probably counter-intuitive and weird—but we've just gotten used to it (in a perfect world, truncating division would use a different function or operator and / would either be undefined for Int or return an optional).

3 Likes

This topic seems to have digressed from whether or not this should be included at all to whether or not “isDivisble” should mean what it does in 99% of other languages or if we should bikeshed on a potential function name because it’s literal meaning may cause confusion. I think the overwhelming majority do not find “isDivisible” to be confusing. It matches the precedences of almost every other language out there and as many have stated, it is common enough that you dont need a degree in number theory to understand it in relation to integer math.

I think that isEven/isOdd are worthwhile inclusions based on the fact that apparently there are a lot of “incorrect” (meaning that they only properly function for positive numbers) implementations. Also, a computed var is so much cleaner than % 2 == 0 and works better with optional chaining.

Incorrect Example

ie:

if let num = optionalNum, num % 2 == 0 { // do something }

vs.

if optionalNum?.isEven { // do something }

The above code actually doesnt compile.

The implementations are minimal and there is precedence in other languages (like ruby) so I feel there isnt any problem with adding these to the standard library.

Many people have stated that almost all the uses of isEven or isOdd are theoretical or for tests only and that it isnt worthwhile to add something that is used so little.

I dont necessarily have a concrete usage example, but if it is decided not to add isEven/isOdd then I would like for isDivisible to be added because it is far more generic and can be used in a much larger set of operations.

2 Likes

This doesn't compile because the result of optionalNum?.isEven is Bool?. You would still have to unwrap this first and then ask whether or not if it's even.

if let num = optionalNum, num.isEven { // do something }

Good point. Not sure why I thought that was valid

You'll probably have to add ?? false to make the compiler happy, and the chaining is even more convenient with a method that's missing as well (if optionalNum?.mod(2) == 0 - although you may argue that it's not that nice to automatically turn 0 into an Optional...)

I think the biggest problem here is that there are no consistent and universally accepted rules for inclusion in the stdlib - and there most likely never will be such a set of rules, because many criterions are highly subjective.

I guess when you compare the amount of time spend in discussion with the actual work needed to add two tiny properties, they should have been included in April ;-)

3 Likes

The good, bad, and ugly of Swift Evolution. (;

1 Like

I honestly wish nil was falsey like other languages. Python treats None as falsey, ruby treats nil, etc.

But this is a discussion for a separate time, which has probably already been had...

Perhaps, but not for the purpose of evaluating whether a number is odd or even. Use modulo when you need to split a table or get remainders; that's what it's useful for.
Asking whether a number is even is quite different from asking whether a number modulus 2 is equal to zero — or whether that number and 1 is equal to zero. Just as isEmpty is usually equal to testing the count of a collection but isn't as succinct and introduces possible side-effects that the compiler must step in and optimise away.

4 Likes

isEmpty and count is not directly analogous to isEven and %. The reason isEmpty exists is because not all collections are random-access, so their count property is O(n). It is therefore a major performance error with types like String to use count to detect emptiness. This isn't something the compiler can optimize away – it's a fundamental characteristic of non-random-access collections. isEmpty is implemented to compare startIndex to endIndex, which can happen in constant time on all collections (though it's occasionally micro-optimized to do something different when it might be faster – for example, it's hard-coded to false on closed ranges). But the method exists primarily to help people not fall into the performance trap. If all collections were random access, the justification would be purely on readability grounds (and fairly thin, given the readability of the count == 0 idiom is pretty good).

There is an analogy to be made between isEmpty and isDivisible(by:), for the case of non-trivial number types like a BigNum. But not for isEven specifically.

7 Likes

But not everybody knows that. It's not surprising that people develop alternative rationales for the count/isEmpty couple. Those alternative rationales may not be firmly grounded as yours. But they may be internally consistent as well, don't you think? This is the organic side of the language. It speaks an interesting language as well, don't you think?

1 Like