# Would it be possible to update the documentation to include how to implement isequal() and hash()

**URL:** <https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518>\
**Category:** General Usage\
**Created:** [January 18, 2021, 5:25am UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518 "2021-01-18T05:25:49Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [January 18, 2021, 5:25am UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/1 "2021-01-18T05:25:49Z")

</div>

Both the C# and Python documentation include clear instructions on how to implement equals and hash code methods and I was wondering if we could do the same for Julia.

For example, the [Base.hash()](https://docs.julialang.org/en/v1/base/base/#Base.hash) documentation says:

> New types should implement the 2-argument form, typically by calling the 2-argument `hash` method recursively in order to mix hashes of the contents with each other (and with `h` ).

It might be helpful to see an example on how to implement the 2-argument form that the documentation talks about.

Same thing with [Base.isequal()](https://docs.julialang.org/en/v1/base/base/#Base.isequal), the documentation shows how to use it, but not how to implement for a custom type.

For example, we could create a `struct` that represents a `Person`:

```julia
struct Person
    first_name::String
    last_name::String
    Age:: Int32
end

```

To override equals we can do:

```julia
import Base.==

function ==(x::Person, y::Person)

    return (x.first_name == y.first_name) && 
           (x.last_name == y.last_name) && 
           (x.age == y.age)
end

```

Not sure if I did that right, but a correct example like this might be helpful.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [January 18, 2021, 12:41pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/2 "2021-01-18T12:41:13Z")

</div>

I think you can find correct examples in [AutoHashEquals](https://github.com/andrewcooke/AutoHashEquals.jl), a package that provides a macro for automatically implementing `==` and `hash` for `struct`s.

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [January 18, 2021, 5:07pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/3 "2021-01-18T17:07:34Z")

</div>

Just out of curiosity, did I implement my equals method correctly? I figured it out by reading the documentation of C# and Python, understanding the logic, and attempting to do the same in Julia.

I was reading this [link](https://stackoverflow.com/a/50056432) about implementing [`Base.hash()`](https://docs.julialang.org/en/v1/base/base/#Base.hash), but I’m not sure if it’s done correctly.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 18, 2021, 5:51pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/4 "2021-01-18T17:51:21Z")

</div>

> [@anon60034542](#):
>
> Just out of curiosity, did I implement my equals method correctly?

Seems correct to me. If it was only for style, I would break that long single-liner.  
You could also write a generic comparison for the case you add fields to `Person`:

```julia
 function ==(x::Person,y::Person)
   for field in fieldnames(Person)
     if getfield(x,field) != getfield(y,field)
       return false
     end
   end
   return true
 end

```

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [January 18, 2021, 6:06pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/5 "2021-01-18T18:06:27Z")

</div>

`isequal` and `==` are two different functions. Which one are you talking about?

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [January 18, 2021, 6:23pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/6 "2021-01-18T18:23:02Z")

</div>

> [@anon60034542](#):
>
> Just out of curiosity, did I implement my equals method correctly? I

I believe it is correct. @lmiq gave a good generic implementation. In fact, you can use such implementation for any composite type for which equality is the same as the equality of all its fields. If you care about the performance of the equality, I would suggest writing it by hand (like you did) and ordering the comparisons in a way that puts first the fields that have the largest probability of being different when the objects are different (or the cheapest to compare, like `Int` fields, if all of them have similar distributions).

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [January 18, 2021, 6:47pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/7 "2021-01-18T18:47:00Z")

</div>

I wouldn’t recommend to use `@leandromartinez98` proposal in a tight loop though, since it suffers from type instability. It is related to the fact, that `getfield(x, field)` is type unstable.

```julia
struct Person
    first_name::String
    last_name::String
    Age::Int32
    X::Bool
    Y::Int
    Z::Float64
end

function Base.:(==)(x::Person,y::Person)
    for field in fieldnames(Person)
        if getfield(x,field) != getfield(y,field)
            return false
        end
    end
    return true
end

p1 = Person("Foo", "Bar", 10, true, 5, 0.35)
p2 = Person("Foo", "Bar", 10, true, 5, 0.29)

julia> @code_warntype p1 == p2
Variables
  #self#::Core.Const(==)
  x::Person
  y::Person
  @_4::Union{Nothing, Tuple{Symbol, Int64}}
  field::Symbol

Body::Bool
1 ─ %1 = Main.fieldnames(Main.Person)::Tuple{Vararg{Symbol}}
│ (@_4 = Base.iterate(%1))
│ %3 = (@_4 === nothing)::Bool
│ %4 = Base.not_int(%3)::Bool
└── goto #6 if not %4
2 ┄ %6 = @_4::Tuple{Symbol, Int64}::Tuple{Symbol, Int64}
│ (field = Core.getfield(%6, 1))
│ %8 = Core.getfield(%6, 2)::Int64
│ %9 = Main.getfield(x, field)::Any
│ %10 = Main.getfield(y, field)::Any
│ %11 = (%9 != %10)::Any
└── goto #4 if not %11
3 ─ return false
4 ─ (@_4 = Base.iterate(%1, %8))
│ %15 = (@_4 === nothing)::Bool
│ %16 = Base.not_int(%15)::Bool
└── goto #6 if not %16
5 ─ goto #2
6 ┄ return true

```

And

```julia
using BenchmarkTools
f(p1, p2) = p1 == p2

julia> @btime f($p1, $p2)
  1.264 μs (15 allocations: 672 bytes)

```

So it is always better to write it by hand. But good news is that you rarely need it for immutable structures, since default equality and hash is good enough.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 18, 2021, 6:56pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/8 "2021-01-18T18:56:17Z")

</div>

> [@Skoffer](#):
>
> It is related to the fact, that `getfield(x, field)` is type unstable.

Nice, good to know.

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [January 18, 2021, 7:15pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/9 "2021-01-18T19:15:13Z")

</div>

But this generic approach is a good application of macro, because this is exactly case of dumb copypaste

```julia
macro equality(T, x, y)
    q = :()
    for field in fieldnames(getfield(@ __MODULE__ , T))
        q = quote
            $q
            if $(esc(x)).$field != $(esc(y)).$field
                return false
            end
        end
    end

    return q
end

function Base.:(==)(p1::Person, p2::Person)
    @equality Person p1 p2
    return true
end

julia> @btime f($p1, $p2)
  8.713 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [January 18, 2021, 7:59pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/10 "2021-01-18T19:59:16Z")

</div>

> [@Skoffer](#):
>
> But this generic approach is a good application of macro, because this is exactly case of dumb copypaste

Seems like we have done the full circle, as my first comment in this thread links to a package that provides a macro for implementing `==` and `hash` automatically.

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [January 18, 2021, 8:01pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/11 "2021-01-18T20:01:18Z")

</div>

Nice 🙂

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 19, 2021, 1:16pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/12 "2021-01-19T13:16:36Z")

</div>

> [@anon60034542](#):
>
> It might be helpful to see an example on how to implement the 2-argument form that the documentation talks about.

You can see all the methods with `methods(Base.hash)`, and just pick one with `@edit` or similar. Eg

```julia
@edit hash(1 => 2, UInt(0))

```

will take you to the method for `Pair` (your IDE may just support clicking on the result of`methods` directly).

Generally, the Julia source has plenty of examples of these kind of functions, it is worth investing into learning how to navigate it. While examples could be extracted into docstrings, real-life examples should be at least as good as toy ones.

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [January 19, 2021, 5:14pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/13 "2021-01-19T17:14:09Z")

</div>

Despite the problem mentioned above, I think it’s still pretty interesting way to solve the problem. Perhaps `getfield()` will one day be stable for types and we can use a `for` loop.

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [January 19, 2021, 5:19pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/14 "2021-01-19T17:19:26Z")

</div>

This is one thing I admire about Julia, even if the documentation isn’t clear, the answer might be somewhere in the ecosystem, and if not, you can ask a question on here and get a quick reply.

This is what frustrated me about Python, while on the surface the documentation seems okay, it’s surprising lacking when you dig deeper. For example, the documentation contains no example of multiple inheritance, some StackOverflow answers are incorrect, and you have to read a blog post by one of the developers to see a basic example of multiple inheritance (which is itself lacking, because it only inherits from one class).

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [January 19, 2021, 5:48pm UTC](https://discourse.julialang.org/t/would-it-be-possible-to-update-the-documentation-to-include-how-to-implement-isequal-and-hash/53518/15 "2021-01-19T17:48:42Z")

</div>

No it wouldn’t, for the very simple reason. Type stability means that for the same type of the arguments, output has the always same type. Input arguments of `getfield(x::T, field::Symbol)` has types of the object (which I denoted here `T`) and `Symbol` - name of the field. But it’s output type is any possible type that can be encountered inside `struct`, so this function is type unstable by design.
