# Why is \`Expr\` mutable?

**URL:** https://discourse.julialang.org/t/why-is-expr-mutable/129850
**Category:** Internals & Design
**Created:** [June 13, 2025, 7:25am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850 "2025-06-13T07:25:40Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)
#### Post date: [June 13, 2025, 7:25am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/1 "2025-06-13T07:25:40Z")

</div>

`Expr` does ~~not have object identity~~ have field equality, i.e.

```julia
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:

```julia
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:

```julia
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
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?

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [June 13, 2025, 7:28am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/2 "2025-06-13T07:28:55Z")

</div>

`===` is the comparison for object identity, and it’ll be `false` for your first example.

---

<div class="post-metadata">

### Author: ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)
#### Post date: [June 13, 2025, 7:57am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/3 "2025-06-13T07:57:17Z")

</div>

> [@PatrickHaecker](#):
>
> So why is `Expr` mutable?

One reason might be that `Expr` existed long before immutable structs were implemented in the language.

---

<div class="post-metadata">

### Author: ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)
#### Post date: [June 13, 2025, 8:52am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/4 "2025-06-13T08:52:29Z")

</div>

> [@Benny](#):
>
> `===` is the comparison for object identity, and it’ll be `false` for your first example.

Thanks for the correction! Do you know of a better term for `==` being `true` although `===` might be `false`?

---

<div class="post-metadata">

### Author: ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)
#### Post date: [June 13, 2025, 9:09am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/5 "2025-06-13T09:09:00Z")

</div>

```julia
julia> a = [1,2,3]; b=[1,2,3];
julia> a == b
true
julia> a === b
false

julia> a = 1; b =1
1
julia> a == b
true
julia> a === b
true

julia> struct Foo
       x::Int64
       end
julia> a = Foo(1); b = Foo(1);
julia> a == b
true
julia> a === b
true

julia> mutable struct Goo
       x::Int64
       end
julia> a = Goo(1); b = Goo(1);
julia> a == b
false
julia> a === b
false

```

---

<div class="post-metadata">

### Author: ![\_bernhard](https://avatars.discourse-cdn.com/v4/letter/_/bc79bd/32.png) [@\_bernhard](https://discourse.julialang.org/u/_bernhard)
#### Post date: [June 13, 2025, 10:37am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/6 "2025-06-13T10:37:25Z")

</div>

Equal, but not identical?

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [June 13, 2025, 11:31am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/7 "2025-06-13T11:31:54Z")

</div>

If `a === b` we say that `a` and `b` are “egal”, whereas if `a == b` we say they are “equal”.

Egality is a “stronger” condition

---

<div class="post-metadata">

### Author: ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)
#### Post date: [June 13, 2025, 12:47pm UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/8 "2025-06-13T12:47:59Z")

</div>

> [@Mason](#):
>
> if `a == b` we say they are “equal”

Thanks to all the suggestions. I needed it as a noun and changed the post to use `field equality`. Sorry for the confusion with the terminology.

Back to the original question: Does anyone know the reason why `Expr` is mutable? I think @GunnarFarneback has a valid point, but I guess there would have been enough time before Julia 1.0 to make `Expr` immutable.

---

<div class="post-metadata">

### Author: ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)
#### Post date: [June 13, 2025, 8:07pm UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/9 "2025-06-13T20:07:58Z")

</div>

I can only guess, but my inkling is that it is some combination of one or more of the following:

- Having `Expr` immutable doesn’t provide much gains in practice.
- Many existing macros would break from the change.
- While `args` is indeed mutable, it’s nevertheless easier to be able to replace it than having to do `empty!` + `append!`.

Potentially there could also be complications in the early parts of bootstrapping, but there I have zero insights.

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [June 14, 2025, 5:42pm UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/10 "2025-06-14T17:42:00Z")

</div>

9 posts were split to a new topic: [How is `Symbol` special?](https://discourse.julialang.org/t/how-is-symbol-special/129885)

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [June 14, 2025, 11:32am UTC](https://discourse.julialang.org/t/why-is-expr-mutable/129850/15 "2025-06-14T11:32:34Z")

</div>

> [@GunnarFarneback](#):
>
> I can only guess, but my inkling is that it is some combination of one or more of the following:

My guess is that it is mostly historical: the `Expr` type dates to the earliest days of Julia, before immutable `struct` types existed in the language, and since they aren’t so performance critical it wasn’t worth the potential breakage to make them immutable once this became possible.
