Credence.Pattern.PreferMapsetForSetEquality (credence v0.8.0)

Copy Markdown

Detects two String.codepoints/1 (or String.graphemes/1) results that are each run through Enum.uniq() |> Enum.sort() and then compared with == for set-equality checking, and rewrites them to compare MapSet.new/1 directly.

Why this matters

The pattern Enum.uniq() |> Enum.sort() builds an unnecessary intermediate list and sort when the only goal is to compare two collections for equal membership (set equality). MapSet.new/1 is semantically explicit and avoids the sort overhead.

Safe core

The rule fires only when the compared collections come from String.codepoints/1 or String.graphemes/1. Those always return lists of binaries, so the two formulations agree exactly:

  • list == compares elements with ==, which coerces numbers ([1] == [1.0] is true), whereas MapSet comparison treats 1 and 1.0 as distinct keys (MapSet.new([1]) == MapSet.new([1.0]) is false). Restricting to binary-returning calls removes that divergence — binaries never coerce.
  • Enum.uniq/1 and MapSet.new/1 both deduplicate with ===, so set membership is preserved.

It also fires only on a block of exactly the two sorted-uniq assignments followed by their comparison, so collapsing the block to a single expression cannot drop other statements or break references to the assigned variables.

Bad

first_set = String.codepoints(first) |> Enum.uniq() |> Enum.sort()
second_set = String.codepoints(second) |> Enum.uniq() |> Enum.sort()
first_set == second_set

Good

MapSet.new(String.codepoints(first)) == MapSet.new(String.codepoints(second))