Warn on unnecessarily strict patterns
Motivation
Consider this (silly) example, that a novice or intermediate Haskeller could accidentally write:
import Data.Char (toUpper, toLower)
upperLower :: String -> (String, String)
upperLower = foldr go ("", "")
where
go x (us, ls) = (toUpper x:us, toLower x:ls)
It mostly works, but performance becomes unacceptable on large strings, and it bottoms on infinite strings. The problem is that the pattern match on the tuple is strict. If ~(us, ls)
is used instead of (us, ls)
, then both of these problems are fixed (and this is in fact what is done in things like bimap @(,)
and partitionEithers
). This is subtle enough that I think we should have a warning to detect it (especially since Haskell is mostly lazy-by-default, and people teaching it often leave out the "mostly", because it's only a few edge cases like this one where it's ever strict-by-default).
Proposal
I'd like a new warning to be added to GHC: -Wunnecessarily-strict-patterns
or something. Any time a program contained a pattern that's strict, but can never fail to match (except if the expression is bottom), it would warn that the pattern could be made lazier via ~
. It would fire on (x, y)
, and on ZipList zl
, but not on x:xs
(since it could be []
instead), and not on (Just j, y)
either (since it could be (Nothing, _)
instead. In the rare cases where this behavior is desired, the warning could be silenced by using a !
in front of the pattern instead (assuming BangPatterns
is on). Optionally (and I'm not sure whether this would be feasible), the warning could also be automatically suppressed if GHC can detect that it wouldn't matter, because something in the RHS is strict in part of what was matched (e.g., \(x, _) -> if x then "X" else ""
).