Expr
does not have object identity have field equality, i.e.
julia> Expr(:call, println) == Expr(:call, println)
true
I was surprised to learn that Expr
nevertheless is mutable. I can easily imagine why Expr
does not have object identity (“Oh, no, I do not mean to call this println
method without arguments in the current module, I instead want to call that println
method without arguments in the current module” does not really make sense).
The problem with being mutable is that an immutable struct with an Expr
field now surprisingly gets object identity loses field equality:
struct MyExpr
expr::Expr
end
julia> :block |> Expr |> MyExpr == :block |> Expr |> MyExpr
false
Note, that this is in contrast to Symbol
, which I had expected to being implemented completely parallel to Expr
, but which is immutable and therefore no surprising object identity pops up field equality gets lost:
struct MySymbol
symbol::Symbol
end
julia> MySymbol(:a) == MySymbol(:a)
true
Semantically, I therefore do not understand, why Expr
is mutable.
Looking at its memory structure presented in Julia (I haven’t checked the C
implementation)
julia> using About
julia> Expr |> about
Concrete DataType defined in Core,
Expr <: Any
Struct with 2 fields:
• head Symbol
• args Vector{Any}
julia> sizeof(Expr)
16
it looks like the args can be changed anyway as they are part of the mutable Array
. Only when modifying the head
, which might not happen in isolation too often anyway, a new object were to be constructed if Expr
were immutable. But I think this would be as cheap as it could get for non-trivial immutable objects.
From a performance point of view, I therefore do neither understand, why Expr
is mutable.
So why is Expr
mutable?