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]istrue), whereasMapSetcomparison treats1and1.0as distinct keys (MapSet.new([1]) == MapSet.new([1.0])isfalse). Restricting to binary-returning calls removes that divergence — binaries never coerce. Enum.uniq/1andMapSet.new/1both 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_setGood
MapSet.new(String.codepoints(first)) == MapSet.new(String.codepoints(second))