Skip to content

Extend performance group with list-length anti-pattern hints#1692

Open
mgajda wants to merge 1 commit intondmitchell:masterfrom
mgajda:performance-length
Open

Extend performance group with list-length anti-pattern hints#1692
mgajda wants to merge 1 commit intondmitchell:masterfrom
mgajda:performance-length

Conversation

@mgajda
Copy link
Copy Markdown

@mgajda mgajda commented Apr 20, 2026

Three new opt-in warnings in the performance group targeting lazy-list
O(n) spine-forcing anti-patterns that are not covered by the existing
default-group length/null hints.

Summary

  • map f (reverse x) -> reverse (map f x) -- extends the existing
    "Move reverse out" family (filter, mapMaybe, catMaybes, lefts,
    rights) to cover map, avoiding one intermediate cons spine.
  • head (map f x) -> f (head x) -- drops the fully-allocated mapped
    cons spine when only the first element is consumed.
  • last (map f x) -> f (last x) -- same idea for the last element.

The default group already covers length x {==,>,>=,<,<=,/=} {0,1} and
reverse (reverse x); these three hints address the remaining common
spine-forcing patterns.

Independence

This PR is intentionally independent of other in-flight performance
group proposals (STM-avoidance, strictness/roundtrip, IORef/State/
container folds). If one of those merges first, this branch simply
extends the existing performance group rather than introducing it; if
it merges first, those branches extend the group introduced here. The
three rules added here do not overlap in name, LHS, or RHS with any
rule in those other branches.

Activation

hlint --with-group=performance FILE.hs

Test plan

  • cabal build succeeds
  • hlint --test passes (965 hints, up from 964)
  • New test file tests/flag-with-group-performance-length.test
    covers all three rules; exact column ranges verified
  • Rules do not fire without --with-group=performance (opt-in)
  • No conflict with existing default-group rules on length/null/reverse

Three opt-in warnings addressing lazy-list O(n) spine-forcing
anti-patterns that are not covered by the existing default-group
length/null hints:

- `map f (reverse x)` -> `reverse (map f x)` extends the existing
  "Move reverse out" family (filter, mapMaybe, catMaybes, lefts,
  rights) to cover `map`, avoiding one intermediate cons spine.

- `head (map f x)` -> `f (head x)` and `last (map f x)` -> `f (last x)`
  drop the fully-allocated mapped cons spine when only a single
  element is consumed.

Enabled with `--with-group=performance`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant