What is WHNF of a new type and how does rseq work on a new type?

Since newtype effectively removed at compile time, they have no tricks, just values. So what happens if I ask for it WHNF using rseq ? For example, in

 Sum (lengthyComputation :: Int) `using` rseq 

where Sum is defined as

 newtype Sum a = Sum { getSum :: a } 

will lengthyComputation be evaluated or not? Is this indicated / documented somewhere so that I can count on it?


Update: Let me explain my doubts in more detail. Intuitively said: " newtype is so clear that WHNF is WHNF of what is wrapped inside." But I feel that this is a very fuzzy label, and the reasoning is not so clear. Let me give an example:

For standard data types, WHNF can be defined as a form where we know which constructor was used to construct the value. If, for example, we didn’t have seq , we could create our own

 seqMaybe :: Maybe a -> b -> b seqMaybe Nothing = id seqMaybe _ = id 

and similarly for any data type, simply by matching patterns on one of its constructors.

Now take

 newtype Identity a = Identity { runIdentity :: a } 

and create a similar seqIdentity function:

 seqIdentity :: Identity a -> b -> b seqIdentity (Identity _) = id 

it is clear that nothing is being done here WHNF. (In the end, we always know which constructor was used.) After compilation, seqIdentity will be identical to const id . In fact, it is impossible to create a polymorphic seqIdentity so that it forcibly evaluates the value enclosed inside Identity ! We could define WHNF a from newtype simply as an unmodified value, and it would be consistent. So I think the question is, how is WHNF defined for newtype s? Or is there no strict definition, and the behavior of “this is WHNF of what's inside” is simply considered something obvious?

+8
concurrency haskell lazy-evaluation newtype
source share
1 answer

In the Rename Data Types section of a report

Unlike algebraic data types, the constructor newtype N does not access, so N ⊥ matches ⊥.

Here

 Sum ⊥ = ⊥ 

therefore, the weak head normal form of the new type is WHNF of the wrapped type, and

 Sum (lengthyComputation :: Int) `using` rseq 

evaluates lengthyComputation (when evaluating the whole expression, simple binding

 let x = Sum (lengthyComputation :: Int) `using` rseq 

of course not, but it's the same without the newtype constructor).

definition of equations for seq

 seq ⊥ b = ⊥ seq ab = b, if a ≠ ⊥ 

and therefore

 seq (Sum ⊥) b = ⊥ 

and in

 seq (lengthyComputaton :: Int) b 

seq needs to find out (sorry for the anthropomorphism) whether lengthyComputation :: Int ⊥ or not. To do this, it must evaluate lengthyComputation :: Int .


Re update:

newtype not used, this means that the constructor is not a constructor of values ​​semantically (only syntactically). Pattern matching in the newtype constructor, unlike pattern matching in the data constructor, is not strict. Considering,

 newtype Foo a = Foo { unFoo :: a } -- record syntax for convenience below 

a "pattern matching"

 function :: Foo a -> Bar function (Foo x) = whatever x 

completely equivalent

 function y = let x = unFoo y in whatever x 

A match always succeeds and does not value anything. The constructor only enforces the type , and "pattern matching" on it does not loop the value type.

seq is magic; it cannot be implemented in Haskell. You can write a function that does the same as seq for the data type, for example, your seqMaybe above, in Haskell, but not for the (polymorphic) newtype , because the "pattern matching" in the newtype constructor is not strict. You will have to map the constructor of the wrapped type, but for the polymorphic newtype you don't have them.

+8
source share

All Articles