Credence.Pattern.NoListDuplicateFlatten
(credence v0.7.0)
Copy Markdown
Detects Enum.concat(List.duplicate(list, n)) (or the piped
list |> List.duplicate(n) |> Enum.concat()) and suggests
Enum.flat_map/2 instead.
LLMs frequently produce this two-step pattern when they need to
"tile" or "repeat" a list n times: first List.duplicate/2 to
create a list of copies, then Enum.concat/1 to merge them.
Enum.flat_map/2 does the same work in a single pass.
Why this is narrowed to Enum.concat + variable + literal count
This rule is deliberately restricted to the cases where the rewrite gives the exact same answer for every input:
Enum.concat/1only, neverList.flatten/1.Enum.concatmerges one level;Enum.flat_mapdoes too, so they agree. ButList.flatten/1flattens recursively, soList.flatten(List.duplicate([[1], [2]], 2))is[1, 2, 1, 2]whileEnum.flat_map(1..2, fn _ -> [[1], [2]] end)is[[1], [2], [1], [2]]. Different answer → not rewritten.The repetition count must be a positive integer literal. The rewrite uses
1..n. Forn == 0the original yields[]but1..0is a descending range ([1, 0]) so the rewrite runs twice; for negativen,List.duplicate/2raises but1..nwould not. Only a literaln >= 1is provably safe, so a runtime variable count is left untouched.The duplicated value must be a bare variable.
List.duplicateevaluates its argument once; thefn _ -> value endclosure evaluates itntimes. For an arbitrary expression that changes side effects, so only a side-effect-free variable reference is rewritten.
Bad
list
|> List.duplicate(3)
|> Enum.concat()
Enum.concat(List.duplicate(list, 3))Good
Enum.flat_map(1..3, fn _ -> list end)