Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elixir: how to make a custom type implement ==, <, and >

Background: I'm working on converting this F# discriminated union to Elixir:

type Suit =
| Diamonds
| Clubs
| Hearts
| Spades

I know there are a few different ways to do that, and that's not my question.

The following need to be true:

Diamonds == Diamonds
Diamonds < Hearts
Spades > Hearts

I was surprised to find that Elixir doesn't have a comparable protocol similar to Haskell's Ord typeclass and this implementation on GitHub (with very few stars!)

Is there a well-accepted or idiomatic way to make custom types comparable?

like image 535
Seth Avatar asked Mar 25 '26 10:03

Seth


1 Answers

The idiomatic way to do this is to simply provide equal?/2 and/or compare/2 in the module that defines the custom type. This can be seen frequently in the core libraries and robust third-party libraries.

For example, Date.compare/2 has the following specification

compare(Calendar.date(), Calendar.date()) :: :lt | :eq | :gt

Starting with v1.10.0, Elixir provides handy sorting with Enum.sort/2 for structs implementing compare/2:

defmodule User do
  defstruct [:name]
  def compare(%User{name: n1}, %User{name: n2}) when n1 < n2,
    do: :lt
  def compare(%User{name: n1}, %User{name: n2}) when n1 > n2,
    do: :gt
  def compare(%User{}, %User{}), do: :eq
end

users = [
  %User{name: "john"},
  %User{name: "joe"},
  %User{name: "jane"}
]

Enum.sort(users, {:asc, User})
#⇒ [%User{name: "jane"},
#   %User{name: "joe"},
#   %User{name: "john"}]
like image 93
Seth Avatar answered Mar 28 '26 02:03

Seth



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!