Credence.Pattern.NoListPopAtForAccess
(credence v0.7.1)
Copy Markdown
Detects List.pop_at(list, 0) |> elem(n) used to extract only the head
(n = 0) or only the rest-after-head (n = 1) of a list, and rewrites
it to the direct List accessor.
List.pop_at/2 returns a {popped, rest} tuple; using elem/2 to pull
out just one side allocates that tuple for nothing.
Why List.first / List.delete_at, not hd / tl
The obvious-looking rewrite — hd/1 for elem(0) and tl/1 for
elem(1) — is not behaviour-preserving. List.pop_at/2 tolerates an
empty list (List.pop_at([], 0) is {nil, []}), so:
List.pop_at([], 0) |> elem(0) # => nil but hd([]) raises
List.pop_at([], 0) |> elem(1) # => [] but tl([]) raisesThe accessors that match pop_at's answer on every list — including the
empty list — are List.first/1 (defaults to nil) and List.delete_at/2
(returns [] on an empty list). Both also raise the same
FunctionClauseError as List.pop_at/2 on non-list inputs, so the
raise-on-non-list contract is preserved as well.
Bad
list |> List.pop_at(0) |> elem(0)
list |> List.pop_at(0) |> elem(1)
elem(List.pop_at(list, 0), 1)Good
List.first(list)
List.delete_at(list, 0)