💾 Archived View for gmi.schrockwell.com › posts › 2023-03-05-abusing-elixir-equality.gmi captured on 2024-05-10 at 10:46:08. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
March 5, 2023
Do you find the `==` operator _too strict_? Do you often dream of two values being equal even though they are completely different types, or of maps being equal to absolutely nothing?
Thankfully, Elixir lets us un-import `Kernel.==/2` and define our own.
defmodule JSEquality do def a == b, do: equal?(a, b) or equal?(b, a) def a != b, do: not __MODULE__.==(a, b) def a === b, do: strictly_equal?(a, b) or strictly_equal?(b, a) def a !== b, do: not __MODULE__.===(a, b) defp equal?(1, x) when x in [true, 1], do: true defp equal?(0, x) when x in [false, 0], do: true defp equal?("1", x) when x in [true, 1, "1"], do: true defp equal?("0", x) when x in [false, 0, "0"], do: true defp equal?("", x) when x in [false, 0, ""], do: true defp equal?(:undefined, x) when x in [nil, :undefined], do: true defp equal?(:Infinity, :Infinity), do: true defp equal?(:negative_Infinity, :negative_Infinity), do: true defp equal?([], x) when x in [false, 0, ""], do: true defp equal?([[]], x) when x in [false, 0, ""], do: true defp equal?([0], x) when x in [false, 0, "0"], do: true defp equal?([1], x) when x in [true, 1, "1"], do: true defp equal?(:NaN, _), do: false defp equal?(int, x) when is_integer(int), do: Kernel.==(int, x) or Kernel.==(Integer.to_string(int), x) defp equal?(list, _) when is_list(list), do: false defp equal?(map, _) when is_map(map), do: false defp equal?(x, x), do: true defp equal?(_, _), do: false defp strictly_equal?(list, _) when is_list(list), do: false defp strictly_equal?(map, _) when is_map(map), do: false defp strictly_equal?(:NaN, _), do: false defp strictly_equal?(x, x), do: true defp strictly_equal?(_, _), do: false end
And, in use:
iex> import Kernel, except: [==: 2, !=: 2, ===: 2, !==: 2] Kernel iex> import JSEquality JSEquality iex> "hello" == "hello" true iex> "hello" == :world false iex> :undefined == nil true iex> "1" == [1] true iex> %{} == %{} false iex> false == "0" true iex> [] == [] false iex> [[]] == false true iex> "" == 0 true iex> -123 == "-123" true iex> :NaN == :NaN false iex> "foo" === "foo" true iex> -1 === "-1" false iex> [] === [] false