In what way are Julia sets immutable?

Viewed 194

If I create a set in Julia, then Julia will tell me that the set is immutable.

julia> pets = Set(["dog", "cat", "budgerigar"])
Set{String} with 3 elements:
  "cat"
  "budgerigar"
  "dog"

julia> ismutable(pets)
false

Nonetheless, I can modify the set in place.

julia> push!(pets, "orangutan")
Set{String} with 4 elements:
  "orangutan"
  "cat"
  "budgerigar"
  "dog"

And I can check that the set contents have changed.

julia> display(pets)
Set{String} with 4 elements:
  "orangutan"
  "cat"
  "budgerigar"
  "dog"

Similarly, I can delete from the set in place

julia> delete!(pets, "dog")
Set{String} with 3 elements:
  "orangutan"
  "cat"
  "budgerigar"

So my question is, in what way are sets immutable? In what way is their mutability different when compared with dictionaries?

julia> ismutable(Dict())
true

What am I not understanding?

1 Answers

If you check the source for Set, you can see that a Set is just a wrapper on a Dict{T,Nothing}, and when you add a new item, say x::T, to the Set, julia just creates a new entry in the Dict of x => nothing. That is, the items are stored in the keys of the Dict, and the values are not relevant so are set to nothing.

Clearly, a Dict needs to be mutable, as you observed in the question. The Set itself does not need to be mutable, since all the mutation is performed within the Dict that is wrapped by the Set. To see what I mean, we can mess around with the internals of a Set.

julia> s = Set(["a", "b"])
Set{String} with 2 elements:
  "b"
  "a"

julia> s.dict
Dict{String,Nothing} with 2 entries:
  "b" => nothing
  "a" => nothing

julia> push!(s, "c")
Set{String} with 3 elements:
  "c"
  "b"
  "a"

julia> s.dict["d"] = nothing

julia> s.dict
Dict{String,Nothing} with 4 entries:
  "c" => nothing
  "b" => nothing
  "a" => nothing
  "d" => nothing

julia> s.dict = Dict("a new set"=>nothing)
ERROR: setfield! immutable struct of type Set cannot be changed
Stacktrace:
 [1] setproperty!(::Set{String}, ::Symbol, ::Dict{String,Nothing}) at ./Base.jl:34
 [2] top-level scope at REPL[14]:1

The key insight here is that I can play around with the mutability of s.dict as much as I want. But because Set is immutable, I can't replace s.dict with an entirely new Dict. That triggers the error you see in my session above.

If it isn't clear what it means for an immutable type to have mutable internals, I asked a similar question about this years ago on StackOverflow. It can be found here

Related