Per the section on Datatype renamings in the report,
Unlike algebraic datatypes, the newtype constructor N is unlifted, so that N ⊥ is the same as ⊥.
Here
Sum ⊥ = ⊥
so the weak head normal form of a newtype is the WHNF of the wrapped type, and
Sum (lengthyComputation :: Int) `using` rseq
evaluates lengthyComputation
(when the entire expression is evaluated, a mere binding
let x = Sum (lengthyComputation :: Int) `using` rseq
of course doesn't, but that is the same without the newtype constructor).
The defining equations for seq
are
seq ⊥ b = ⊥
seq a b = b, if a ≠ ⊥
and hence
seq (Sum ⊥) b = ⊥
and in
seq (lengthyComputaton :: Int) b
the seq
is required to find out (sorry for the anthropomorphism) whether lengthyComputation :: Int
is ⊥ or not. To do that, it must evaluate lengthyComputation :: Int
.
Re update:
newtype
s are unlifted, that means that the constructor is not a value constructor semantically (only syntactically). Pattern-matching on a newtype
constructor is, in contrast to pattern matching on a data
constructor not strict. Given
newtype Foo a = Foo { unFoo :: a } -- record syntax for convenience below
a "pattern match"
function :: Foo a -> Bar
function (Foo x) = whatever x
is completely equivalent to
function y = let x = unFoo y in whatever x
The match always succeeds, and evaluates nothing. The constructor only coerces the type and "pattern matching" on it un-coerces the type of the value.
seq
is magic, it cannot be implemented in Haskell. You can write a function that does the same as seq
for a data
type, like your seqMaybe
above, in Haskell, but not for a (polymorphic) newtype
, because "pattern matching" on the newtype constructor is not strict. You would have to match on the constructor(s) of the wrapped type, but for a polymorphic newtype
, you don't have them.