Skip to content

make numbers non-iterable? #7903

Closed
Closed
@StefanKarpinski

Description

@StefanKarpinski

@StephenVavasis has pointed out some rather confusing behavior of the in operator, including:

julia> VERSION
v"0.3.0-rc2+12"

julia> x = IntSet([3,5])
IntSet([3, 5])

julia> in(3,x)
true

julia> in(x,3)
false

julia> in("abc",19)
false

julia> in(19,"abc")
false

Worse still is this:

julia> 97 in "abc"
true

This issue is to discuss what, if anything, we can do to reduce some of this confusion.

Activity

JeffBezanson

JeffBezanson commented on Aug 7, 2014

@JeffBezanson
SponsorMember

x in y is pretty simple: does x == any element iterated by y.

The issue is not specific to in; 97 == 'a' is true.

I do think it would be good to make Char not an integer type. In theory having numbers not be iterable might be ok, but I think it will be very annoying in practice.

see also #5844

StephenVavasis

StephenVavasis commented on Aug 8, 2014

@StephenVavasis
Contributor

So I guess the problem is not specifically with `in' but with ==. I raised the issue that apparently meaningless uses of 'in' do not generate errors because I made silly blunder with 'in' in my project that did not generate an error message. For a similar reason (ease of debugging) the following uses of == should also generate errors, no?

julia> [3,4,5] == ["x","y"]
false

julia> IntSet() == ["a"=>7]
false

julia> 9 == Int64
false

julia>

JeffBezanson

JeffBezanson commented on Aug 8, 2014

@JeffBezanson
SponsorMember

Our == is total, defined on all pairs of values. I find this convenient,
and I think it is quite difficult to decide exactly which cases to exclude.
On Aug 7, 2014 8:15 PM, "StephenVavasis" notifications@github.com wrote:

So I guess the problem is not specifically with `in' but with ==. I raised
the issue that apparently meaningless uses of 'in' do not generate errors
because I made silly blunder with 'in' in my project that did not generate
an error message. For a similar reason (ease of debugging) the following
uses of == should also generate errors, no?

julia> [3,4,5] == ["x","y"]
false

julia> IntSet() == ["a"=>7]
false

julia> 9 == Int64
false

julia>


Reply to this email directly or view it on GitHub
#7903 (comment).

StephenVavasis

StephenVavasis commented on Aug 8, 2014

@StephenVavasis
Contributor

Jeff,

I guess it's OK to make == total, but there also ought to be a restricted
version of == that requires the two operands to have the same type in
order to catch programmer blunders. I would guess that it is rare among
scientific codes to have a need for == with unequal types. Is there such a
restricted version available in Julia? (And is there a similarly
restricted version of 'in'?)

-- Steve

On Thu, 7 Aug 2014, Jeff Bezanson wrote:

Our == is total, defined on all pairs of values. I find this convenient,
and I think it is quite difficult to decide exactly which cases to exclude.
On Aug 7, 2014 8:15 PM, "StephenVavasis" notifications@github.com wrote:

So I guess the problem is not specifically with `in' but with ==. I raised
the issue that apparently meaningless uses of 'in' do not generate errors
because I made silly blunder with 'in' in my project that did not generate
an error message. For a similar reason (ease of debugging) the following
uses of == should also generate errors, no?

julia> [3,4,5] == ["x","y"]
false

julia> IntSet() == ["a"=>7]
false

julia> 9 == Int64
false

julia>


Reply to this email directly or view it on GitHub
#7903 (comment).


Reply to this email directly or view it onGitHub.[7881863__eyJzY29wZSI6Ik5ld3NpZXM6QmVhY29uIiwiZXhwaXJlcyI6MTcyMzA3NzY0NywiZGF0YSI6eyJ
pZCI6MzkxNDA5MjB9fQ==--26abb0bc2f6cb83bf6826a0450d494ff66b93a18.gif]

timholy

timholy commented on Aug 8, 2014

@timholy
SponsorMember

Three =:

julia> 5 == 5.0
true

julia> 5 === 5.0
false
jey

jey commented on Aug 8, 2014

@jey
Contributor

However that is for object identity, not equality. I.e. (zeros(3) ===
zeros(3)) is false

On Thursday, August 7, 2014, Tim Holy notifications@github.com wrote:

Three =:

julia> 5 == 5.0true
julia> 5 === 5.0false


Reply to this email directly or view it on GitHub
#7903 (comment).

tkelman

tkelman commented on Aug 8, 2014

@tkelman
Contributor

One option is you can pick your favorite unicode equality-resembling symbol from this list

'(> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ |.>| |.<| |.>=| |.≥| |.<=| |.≤| |.==| |.!=| |.≠| |.=| |.!| |<:| |>:| ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣))
that is parsed with the same precedence as == but open for user definitions, and make that operator error on inputs of different types.

StephenVavasis

StephenVavasis commented on Aug 8, 2014

@StephenVavasis
Contributor

Earlier I wrote that it seems OK for == to work for all possible operands, but now I changed my mind. The (small) increase in expressive power does not offset the potential for enabling programmer blunders. One important mission of a programming language is to help prevent the programmer from shooting himself/herself in the foot, and in this case Julia needlessly fails to close a loophole.

About 25 years ago when I was a CS prof at Cornell, the issue came up (again) whether to switch our introductory programming course to C, and our faculty unanimously rejected the idea (again) for many reasons. One of the reasons was that we did not savor the possibility of undergrads lined up outside the TA office for help with their assignments because they wrote
"if (x=0)" in their homework (instead of "if (x==0)") . The same rationale still applies to Julia 25 years later; it should not be possible to compile a program with in(x,3) where x is an IntSet, nor a program with x==t, where x is an Int and t is a Dict.

nalimilan

nalimilan commented on Aug 8, 2014

@nalimilan
Member

It seems it would be better to add restrictions to in than to ==. For example, in could require that the second argument is a collection rather than a scalar.

I also agree that Char shouldn't be considered as an integer type. There's Uint8 for this use case.

JeffBezanson

JeffBezanson commented on Aug 8, 2014

@JeffBezanson
SponsorMember

I find the case for restricting in much more convincing than for ==.

With ==, people will write code to look for a certain value, like x == 0. In a very generic context, you might not know what x could be. This applies even more for sentinel values, e.g. x == :none. It would be too much of a gotcha to require also checking that the comparison will be valid, e.g. if isa(x,Symbol) && x == :none. Also imagine a Dict{Any,Any}, where arbitrary keys need to be compared.

In other words, it would be too difficult to get back the current behavior. You would need something like if comparable(x,y) && x==y, but it's not clear how to write comparable. In contrast, it is currently fairly easy to add extra restrictions if you want.

ckhroulev

ckhroulev commented on Aug 9, 2014

@ckhroulev

Another (unrelated) issue in() seems to have is that in(x, s::Set) calls haskey to check equality (see set.jl:16), ignoring user-defined == and isequal.

Please let me know what you think about this, and feel free to move this elsewhere if appropriate.

Here's an example:

julia> VERSION
v"0.2.1"

julia> immutable Edge # edges of an undirected graph
       a :: Integer
       b :: Integer
       end

julia> ==(a::Edge, b::Edge) = (a.a == b.a && a.b == b.b) || (a.a == b.b && a.b == b.a)
== (generic function with 47 methods)

julia> import Base.isequal

julia> isequal(a::Edge, b::Edge) = a == b
isequal (generic function with 34 methods)

julia> edges = [Edge(1,2), Edge(2,3), Edge(3,1), Edge(1,3)]
4-element Array{Edge,1}:
 Edge(1,2)
 Edge(2,3)
 Edge(3,1)
 Edge(1,3)

Now, compare

julia> in(Edge(2,1), edges)
true

and

julia> in(Edge(2,1), Set(edges))
false

I did not expect this.

This also breaks unique (try unique(edges)), which uses in(x, s::Set) internally.

In this particular case a simple workaround is to define the (inner) constructor

Edge(a::Integer, b::Integer) = a < b ? new(a,b) : new(b,a)

and use default == and isequal, but a solution like this one may not be possible in for other user-defined types. (This reminds me of a discussion of the relationship between "a == b" and "hash(a) == hash(b)".)

PS: As far as I can tell recent code in the master branch should show the same behavior.

JeffBezanson

JeffBezanson commented on Aug 9, 2014

@JeffBezanson
SponsorMember

You also need to implement hash for sets and dicts to work properly with user-defined ==.

jey

jey commented on Aug 9, 2014

@jey
Contributor

Hm, perhaps Set should be called HashSet to preserve the mathematical meaning of "Set"? Overall, the mathy types aren't fully internally consistent, IMO.

89 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingThis change will break codehelp wantedIndicates that a maintainer wants help on an issue or pull requestneeds decisionA decision on this change is needed

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      make numbers non-iterable? · Issue #7903 · JuliaLang/julia