Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should true == 1? #15983

Closed
benkamphaus opened this issue Apr 21, 2016 · 16 comments
Closed

Should true == 1? #15983

benkamphaus opened this issue Apr 21, 2016 · 16 comments

Comments

@benkamphaus
Copy link

@vtjnash recommended I write a bug report after encountering this behavior and looking into workarounds.

At present:

true == 1
# true

This seems fairly jarring given that the Bool type seems much more restrictive than in languages like e.g. Python where implicit promotion/deference to parent methods for comparison are typical. For example, this:

if 1
    println("Ok!")
end
# ERROR: TypeError: non-boolean (Int64) used in boolean context

Would also imply to me (not necessarily in any formal sense, though) that the prior equality test should also result in a TypeError.

In this case I want to be able to put both 1 and true in a Set of type Any:

Set([1, true, "true"])
# Set(Any["true",true])

But due to hash/equality semantics, only one will make it in. I could return an alternative implementation of Set backed by an ObjectIdDict for my use case, which is a serialization library that will convey and receive sets from other languages (whose Boolean equality/hash semantics are heterogenous), but wanted to check in to see if this is the desired behavior, or falling out of some implementation detail.

@StefanKarpinski
Copy link
Member

StefanKarpinski commented Apr 21, 2016

It comes from the fact that Bool is essentially just UInt1 – a one-bit unsigned integer. The true == 1 part doesn't really bother me. The principle is that it's ok to use a boolean where a number is expected, but it's not ok to use a non-boolean where a boolean is expected. This is often handy, e.g. when counting the number of values that are true using sum. The Set business is more concerning. We could make Bool a non-numeric type, but that would have other consequences, e.g. it would no longer be possible to have im = Complex(false,true), although we've discussed recently whether that's a good idea.

@eschnett
Copy link
Contributor

In a set you'll also run into problems if you want to add 1 and 1.0 at the same time. You should be able to use isequal (i.e. ===) instead of == to compare values in a set.

How does Set handle NaN? Technically, NaN != NaN, and it should thus keep all of them?

@benkamphaus
Copy link
Author

@eschnett Just to be clear, since the Set impl is a HashSet - backed by a Dict's keys with all values as Void, the important detail is how each value hashes:

julia> Set([NaN, NaN])
Set([NaN])

julia> hash(NaN)
0x15d7d083d04ecb90

julia> hash(1)
0x02011ce34bce797f

julia> hash(true)
0x02011ce34bce797f

The Set is the concerning part for me as well @StefanKarpinski (impact is it will require a workaround that impacts usability when edge case is encountered). Maybe I should revise issue title to reflect Set as focus? The behavior of equality came out of the discussion on irc.

@eschnett
Copy link
Contributor

I was confused; of course, isequal(NaN, NaN) is true.

@vtjnash
Copy link
Member

vtjnash commented Apr 21, 2016

note that Char has a similar issue, and also fails for transitive equality with non-integer types 32.0 != ' ' == 32

The true == 1 part doesn't really bother me. The principle is that it's ok to use a boolean where a number is expected, but it's not ok to use a non-boolean where a boolean is expected.

in comparing bool == foo, I think it's arguably expected that foo::Bool

@benkamphaus
Copy link
Author

Yeah, I think it's even more surprising with Char in the Set context:

> Set([32, ' '])
# Set(Any[' '])

Probably more plausibly encountered in a scenario like:

> x = " foo"[1]
# ' '
> Set([32, x])
# Set(Any[' '])

@JeffBezanson
Copy link
Member

Since Char isn't a subtype of Number (unlike Bool) I agree ' ' == 32 doesn't seem right.

@StefanKarpinski
Copy link
Member

The char vs number thing is definitely a bug left over from when Char was a kind of integer. I'm working on a fix.

@StefanKarpinski
Copy link
Member

The Char vs. Int comparison thing is surprisingly annoying to fix – we assume that you can compare integers and chars all over the place. I've almost got it done, but it makes me wonder about the change.

@eschnett
Copy link
Contributor

I think it's still the right way to go. Unlike e.g. for real and integer numbers, there is not commonly used abstraction that makes characters a subset of integers. ASCII and UTF-8 are common, but they specify an encoding, not an equivalence. It's easy enough to convert between characters and integers, and if a certain piece of code requires this too often, then I wonder whether there's an abstraction missing.

Most other languages -- Python, Fortran, Mathematica -- don't allow such comparisons either. C and its descendents seem to be in the exception.

@stevengj
Copy link
Member

"there is not commonly used abstraction that makes characters a subset of integers" .... Unicode?

@simonster
Copy link
Member

In any case, they don't need to be isequal, which would fix the Set issue.

@nalimilan
Copy link
Member

Indeed Unicode offers a standard mapping from integer to char, but comparison between these types is still quite confusing. +1 for deprecating it, and requiring people to write Char(32) == ' ' when that's really what they want.

@stevengj
Copy link
Member

stevengj commented Apr 22, 2016

I don't know... what languages have true character types that don't allow such comparisons? Python doesn't have a character type per se, it only has length-1 strings, and Fortran is similar if I understand it correctly. I don't know about Mathematica, but Mathematica is not particularly well-known for its strength in string processing.

In any case, I feel like discussion of Char == Integer should be in a separate issue.

@Jeffrey-Sarnoff
Copy link

" it would no longer be possible to have im = Complex(false,true), although we've discussed recently whether that's a good idea."
Assuming changing that is a good idea, imo the change should still allow
[U]Int +,-,* Bool, Bool +,-,* [U]Int with true working as 1 and false working as 0
by overloading those signatures (as they are so handy, at times).

vtjnash added a commit that referenced this issue May 20, 2016
vtjnash added a commit to vtjnash/julia that referenced this issue May 31, 2016
it would now be possible to make == with non-comparable items an error
since these notions are no longer coupled

fix JuliaLang#9381
ref JuliaLang#15983
vtjnash added a commit that referenced this issue Jun 4, 2016
it would now be possible to make == with non-comparable items an error
since these notions are no longer coupled

fix #9381
ref #15983
vtjnash added a commit that referenced this issue Jun 4, 2016
it would now be possible to make == with non-comparable items an error
since these notions are no longer coupled

fix #9381
ref #15983
vtjnash added a commit that referenced this issue Jun 4, 2016
it would now be possible to make == with non-comparable items an error
since these notions are no longer coupled

fix #9381
ref #15983
vtjnash added a commit that referenced this issue Jun 7, 2016
it would now be possible to make == with non-comparable items an error
since these notions are no longer coupled

fix #9381
ref #15983
@StefanKarpinski
Copy link
Member

This is decided and consistent in 1.0: yes, true == 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants