In Haskell, is there ever a situation where for a data type
{-# LANGUAGE BangPatterns #-}
import Control.DeepSeq
data D = D Int
the instance
instance NFData D where
rnf (D !_) = ()
can have a different effect than the instance with another outer !
:
instance NFData D where
rnf !(D !_) = ()
My research:
let
bindings (like this answer), which I think doesn't apply for function pattern matches like this.https://prime.haskell.org/wiki/BangPatterns#Thebasicidea says
A bang only really has an effect if it precedes a variable or wild-card pattern
and
putting a bang before a pattern that forces evaluation anyway does nothing
and I think
rnf (D _)
already forces evaluation anyway
rnf x = case x of D _ -> ...
rnf !(D _)
would have the same effect as rnf (D _)
rnf !(D !_)
must have the same effect as rnf (D !_)
So I think no, these two are always equivalent, but I'm asking anyway to have one super clear answer to refer people to.
Indeed this is correct. We can see what is evaluated using :sprint
in GHCi
, which shows us what thunks have been evaluated.
With no bang patterns:
λ data D = D Int
λ d1 = D 1
λ :sprint d1
d1 = _
λ f1 (D _) = 0
λ f1 d1
0
λ :sprint d1
d1 = <D> _ -- Only D evaluated
With an inner bang pattern:
λ d2 = D 2
λ :sprint d2
d2 = _
λ f2 (D !_) = 0
λ f2 d2
0
λ :sprint d2
d2 = <D> 2 -- Everything evaluated
With an outer bang pattern:
λ d3 = D 3
λ :sprint d3
d3 = _
λ f3 !(D _) = 0
λ f3 d3
0
λ :sprint d3
d3 = <D> _ -- Only D evaluated
With an inner and outer bang patterns:
λ d4 = D 4
λ :sprint d4
d4 = _
λ f4 !(D !_) = 0
λ f4 d4
0
λ :sprint d4
d4 = <D> 4 -- Everything evaluated
From this we can easily see that the patterns !(D !_)
and (D !_)
are equivalent, and moreover that patterns of the form !(D ...)
are redundant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With