[Discuss newtypes. Bryan O'Sullivan **20080503061620] { hunk ./en/book-shortcuts.xml 158 +undefined"> hunk ./en/ch07a-json-typeclass.xml 241 + + + + How to give a type a secret identity + + In addition to the familiar &data; keyword, Haskell provides + us with another way to create a new type, via &newtype;. + + &Newtype.hs:newtype; + + The purpose of a &newtype; declaration is to rename an + existing type, giving it a distinct identity. As we can see, it + is similar in appearance to a type declaration using the &data; + keyword. + + When we declare a &newtype;, we can choose to make any of + the underlying type's typeclass instances available. Here, we've + elected to make NewtypeInt provide + Int's instances for Eq, + Ord and Show. As a result, we can + compare and print values of type NewtypeInt. + + &newtype.ghci:compare; + + However, since we are not exposing + Int's Num or Integral + instances, values of type NewtypeInt are not + numbers. For instance, we can't add them. + + &newtype.ghci:num; + + As with the &data; keyword, we can use a &newtype;'s value + constructor to create a new value, or to pattern match on an + existing value. + + + Differences between data and newtype declarations + + The &newtype; keyword exists to give an existing type a + new identity, and it is more restricted in how we can use it + than the &data; keyword. Specifically, a &newtype; can only + have one value constructor, and that constructor must have + exactly one field. + + &NewtypeDiff.hs:newtype; + + Beyond this, there's another important difference between + &data; and &newtype;. A type created with the &data; keyword + has some runtime book-keeping overhead, to record which + constructor a value was created with. A &newtype; value, on + the other hand, can only have one constructor, and so does not + need this overhead. This makes it more space- and + time-efficient at runtime. + + Because a &newtype;'s constructor is used only at compile + time and doesn't have a runtime representation, pattern + matching on &undefined; behaves differently. + + To understand the difference, let's first review what we + might expect with a normal datatype. We are already familiar + with the idea that if &undefined; is evaluated at runtime, it + causes a crash. + + &newtype.ghci:undefined; + + Here is a pattern match where we construct a + DataInt using the D constructor, and + put &undefined; inside. + + &newtype.ghci:D; + + Since our pattern matches against the constructor but + doesn't inspect the payload, the &undefined; remains + unevaluated and does not cause a crash. + + In this example, we're not using the D + constructor, so the unprotected &undefined; gets evaluated + when the pattern match occurs, and we crash. + + &newtype.ghci:data; + + When we use the N constructor, we get the + same behaviour as we did with the D constructor: + no crash. + + &newtype.ghci:N; + + The crucial difference arises when we get rid of the + N constructor, and match against an unprotected + &undefined;. + + &newtype.ghci:newtype; + + We didn't crash! Because there's no constructor present + at runtime, matching against N _ is actually + equivalent to matching against the plain wild card + _: since this always matches, the expression + doesn't need to be evaluated. + + addfile ./examples/ch07/Newtype.hs hunk ./examples/ch07/Newtype.hs 1 +{-- snippet newtype --} +data DataInt = D Int + deriving (Eq, Ord, Show) + +newtype NewtypeInt = N Int + deriving (Eq, Ord, Show) +{-- /snippet newtype --} + +newtype Q = Q DataInt + +an_int :: Int +an_int = 5623756 + +error_int :: Int +error_int = error "crash!" addfile ./examples/ch07/NewtypeDiff.hs hunk ./examples/ch07/NewtypeDiff.hs 1 +{-- snippet newtype --} +-- ok: any number of fields and constructors +data TwoFields = TwoFields Int Int + +-- ok: exactly one field +newtype Okay = ExactlyOne Int + +-- ok: type parameters are no problem +newtype Param a b = Param (Either a b) + +-- ok: record syntax is fine +newtype Record = Record { + getInt :: Int + } + +-- bad: no fields +newtype TooFew = TooFew + +-- bad: more than one field +newtype TooManyFields = Fields Int Int + +-- bad: more than one constructor +newtype TooManyCtors = Bad Int + | Worse Int +{-- /snippet newtype --} addfile ./examples/ch07/newtype.ghci hunk ./examples/ch07/newtype.ghci 1 +:load Newtype + +--# compare +N 1 < N 2 + +--# num +N 313 + N 37 + +--# undefined +undefined + +--# D +case D undefined of D _ -> 1 + +--# data +case undefined of D _ -> 1 + +--# N +case N undefined of N _ -> 1 + +--# newtype +case undefined of N _ -> 1 }