Why a === b returns TRUE when a and b are single elements (numbers or strings)

The other day I was explaining a friend that when you store the same content into different variables, such as:

a = [1, 2]
b = [1, 2]

the two objects can be actually distinguished by:

a === b
false

because they are stored in different sites of the memory.

However, a second example I gave my friend was:

a = 1
b = 1
a === b
true

When we saw true, I was confused and my friend stopped believing me :wink:
The same happens when I try with strings.

What is happening there and does it have something to do with mutable and immutable objects, as mentioned in the docs of ===?

help?> ===
search: === == !==

  ===(x,y) -> Bool
  ≡(x,y) -> Bool


  Determine whether x and y are identical, in the sense that no program could distinguish them. First the types of x and y are compared. If those are
  identical, mutable objects are compared by address in memory and immutable objects (such as numbers) are compared by contents at the bit level. This
  function is sometimes called "egal". It always returns a Bool value.

the vectors are mutable, so their addresses in memory is compared, which means:

julia> a = [1,2]
2-element Vector{Int64}:
 1
 2

julia> b = a

julia> a === b
true

because b is just an alias of a in this case. In your example, they are different, since modifying one of them won’t change the other.


immutable objects (such as numbers) are compared by contents at the bit level

explains the number and string cases, so I think docs explained this perfectly. It has nothing to do with “single element” or not, but scalars are canonical example of immutable, so the hand-wavy generalization of “=== compares scalars by value” is not wrong (edit: technically still wrong, since you can have mutable <: Number, for example BigInt). But all immutable, not just scalars, are compared by bit content:

julia> a = (2,3)
(2, 3)

julia> b = (2,3)
(2, 3)

julia> a === b
true
3 Likes

Thanks, I have the initial confusion cleared up now.

Just for completion, I also tested this:

a = Int32.(10)
b = Int64.(10)
a === b
false

An Int32 has 4 bytes, while an Int64 has 8:

julia> bitstring(Int32(10))
"00000000000000000000000000001010"

julia> bitstring(Int64(10))
"0000000000000000000000000000000000000000000000000000000000001010"

julia> sizeof(Int32(10))
4

julia> sizeof(Int64(10))
8

As the bitlengths are not the same, the two are not identical.

Also, you don’t need the . there - while numbers are iterable, they’re treated as a scalar.

not sure what’s the point of dot, you’re operating on scalar. The type is always compared first in ===, if they are not even of the same type, they can’t possibly be “identical”, since Julia goes with nominal typing.

That’s not completely accurate:

julia> BigInt(1) === BigInt(1)
false

Arbitrary-precision numbers in Julia are implemented with mutable structs

1 Like

It’s more than bit length. Int32 and Int64 are not even the same data type.

3 Likes