data/newtype examples

Dear Cafe - I wanted a quick example (for teaching) that shows a difference between data and newtype. You'd think that `newtype` makes things more strict, so we'd see more exceptions. Then try the following, and try to guess the semantics beforehand: ghci> data D = D Bool ghci> case D undefined of D x -> () ghci> case undefined of D x -> () ghci> newtype N = N Bool ghci> case N undefined of N x -> () ghci> case undefined of N x -> () What examples do you use? I'm interested both in simple ones, and confusing/obfuscated ones. Yes I know I can ghci> seq (D undefined) () () ghci> seq (N undefined) () *** Exception: Prelude.undefined but that `seq` is extra magic that needs to be explained. Well, perhaps I should. To show that I've done some homework here: the primary source of truth is the Haskell Language Report (well hidden at the bottomest end of https://www.haskell.org/documentation/) and it uses (in 4.2.3 Datatype Renamings) the concept of "equivalent to bottom". Then 6.2 Strict Evaluation shows how to test that with `seq` (without mentioning newtype, and that's fine since the Report is not a tutorial). This suggests that `seq` is really the way to go here. There are examples for data/newtype/case combined in 3.17.2 Informal Semantics of Pattern Matching but they distinguish between newtype N = N Bool and data D = D !Bool (with a bang). - J.W.

Hi Johannes, Fun examples. For me, the key insight that allowed me to anticipate behaviors here is that Haskell's `case` is lazy. That is, `case undefined of _ -> ()` will evaluate to `()`. Instead, certain patterns force the scrutinee, but it's really the *patterns* that do this forcing, not the `case` itself. In light of this observation -- and knowing that newtypes are completely erased at runtime -- you can then predict the behavior of these examples. (I won't say more, so as not to spoil the fun of experimenting for others.) If you're teaching these examples, you may also want to consider `data S = S !Bool`, whose behavior in these is different from both D and N. Richard
On May 6, 2022, at 3:19 AM, Johannes Waldmann
wrote: Dear Cafe -
I wanted a quick example (for teaching) that shows a difference between data and newtype.
You'd think that `newtype` makes things more strict, so we'd see more exceptions. Then try the following, and try to guess the semantics beforehand:
ghci> data D = D Bool ghci> case D undefined of D x -> () ghci> case undefined of D x -> ()
ghci> newtype N = N Bool ghci> case N undefined of N x -> () ghci> case undefined of N x -> ()
What examples do you use? I'm interested both in simple ones, and confusing/obfuscated ones.
Yes I know I can
ghci> seq (D undefined) () () ghci> seq (N undefined) () *** Exception: Prelude.undefined
but that `seq` is extra magic that needs to be explained. Well, perhaps I should.
To show that I've done some homework here: the primary source of truth is the Haskell Language Report (well hidden at the bottomest end of https://www.haskell.org/documentation/) and it uses (in 4.2.3 Datatype Renamings) the concept of "equivalent to bottom". Then 6.2 Strict Evaluation shows how to test that with `seq` (without mentioning newtype, and that's fine since the Report is not a tutorial). This suggests that `seq` is really the way to go here. There are examples for data/newtype/case combined in 3.17.2 Informal Semantics of Pattern Matching but they distinguish between newtype N = N Bool and data D = D !Bool (with a bang).
- J.W. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (2)
-
Johannes Waldmann
-
Richard Eisenberg