+ +

Redundant Bang Patterns [GHC-38520]

+ +

+ Flag: -Wredundant-bang-patterns
+ + + +

+ + +

The BangPatterns extension allows the user to mark parts of a pattern as strict by prefixing the pattern with an exclamation mark. +By default, Haskell only evaluates an expression as little as it needs to determine whether the pattern matches or not. +Using bang patterns causes the matched expression to always be evaluated to weak head normal +form (WHNF) before the rest of the clauses, any guard patterns, or the right-hand side +of the clause are executed.

+

However, there are cases where a bang pattern can be redundant. +This happens either because a previous match clause already forced the evaluation, because the user is +trying to match on a strict field of a data type, or because the type of the value being +matched on is of an unlifted or unboxed type like Int# or Array#.

+

In all of these cases, the Bang Pattern has no added effect, so it is redundant.

+ + + +

Examples

+ +
+ Already deconstructed +
+

Warning message

+
AlreadyDeconstructed.hs:5:15: warning: [-Wredundant-bang-patterns]
+    Pattern match has redundant bang
+    In an equation for ‘doubleIfTrue’: doubleIfTrue x = ...
+  |
+5 | doubleIfTrue !x         = fst x
+  |               ^
+

Explanation

+

It is possible that a previous clause already forced the evaluation of an expression. +For example, doubleIfTrue’s first clause already deconstructs the pair tuple, so +a bang pattern on the tuple as a whole has no effect in the second clause.

+ +
AlreadyDeconstructed.hs
+
+
+
+
Before
+ +
module AlreadyDeconstructed where
+
+doubleIfTrue :: (Int, Bool) -> Int
+doubleIfTrue (x, y) | y = x * 2
+doubleIfTrue !x         = fst x
+
+
+
+
+
+
After
+ +
module AlreadyDeconstructed where
+
+doubleIfTrue :: (Int, Bool) -> Int
+doubleIfTrue (x, y) | y = x * 2
+doubleIfTrue x          = fst x
+
+
+
+
+ + +
+
+ +
+ Strict fields +
+

Warning message

+
UnliftedTypes.hs:17:6: warning: [-Wredundant-bang-patterns]
+    Pattern match has redundant bang
+    In an equation for ‘foo’: foo a = ...
+   |
+17 | foo !a !b !c = ()
+   |      ^  
+

Explanation

+

Haskell allows a user to annotate fields of a datatype as strict, by prepending +their type with an exclamation mark. +Pattern matching on such a constructor forces it to WHNF, but this also automatically +forces any strict fields to evaluate to WHNF as well. +Thus, a Bang Pattern has no effect on a strict field.

+ +
StrictField.hs
+
+
+
+
Before
+ +
module StrictField where
+
+data Foo = MkFoo !Int Int
+
+foo :: Foo -> Foo -> ()
+foo !a (MkFoo !b !c) = ()
+
+
+
+
+
+
After
+ +
module StrictField where
+
+data Foo = MkFoo !Int Int
+
+foo :: Foo -> Foo -> ()
+foo !a (MkFoo b !c) = ()
+
+
+
+
+ + +
+
+ +
+ Unlifted and unboxed types +
+

Warning messages

+
UnliftedTypes.hs:17:6: warning: [-Wredundant-bang-patterns]
+    Pattern match has redundant bang
+    In an equation for ‘foo’: foo a = ...
+   |
+17 | foo !a !b !c = ()
+   |      ^
+
+UnliftedTypes.hs:17:9: warning: [-Wredundant-bang-patterns]
+    Pattern match has redundant bang
+    In an equation for ‘foo’: foo b = ...
+   |
+17 | foo !a !b !c = ()
+   |         ^
+
+UnliftedTypes.hs:17:12: warning: [-Wredundant-bang-patterns]
+    Pattern match has redundant bang
+    In an equation for ‘foo’: foo c = ...
+   |
+17 | foo !a !b !c = ()
+   |            ^
+

Explanation

+

Forcing the evaluation of a value up to WHNF does not make sense for unlifted and +unboxed types, because these types can never be represented by an unevaluated expression at runtime. +Thus, trying to enforce strictness via a bang pattern has no effect.

+ +
UnliftedTypes.hs
+
+
+
+
Before
+ +
{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE UnliftedNewtypes #-}
+
+module UnliftedTypes where
+
+import GHC.Exts
+
+newtype MyInt :: TYPE 'IntRep where
+  MkMyInt :: Int# -> MyInt
+
+foo :: Int# -> MyInt -> (# Int, Int #) -> ()
+foo !a !b !c = ()
+
+
+
+
+
+
After
+ +
{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE UnliftedNewtypes #-}
+
+module UnliftedTypes where
+
+import GHC.Exts
+
+newtype MyInt :: TYPE 'IntRep where
+  MkMyInt :: Int# -> MyInt
+
+foo :: Int# -> MyInt -> (# Int, Int #) -> ()
+foo a b c = ()
+
+
+
+
+ + +
+
+ + + +