Just like how
you define new data types using data keyword, you can also define new data
types using newtype keyword, only restriction is the type has exactly one
constructor with exactly one field inside it.
For example,
Sample.hs
type FirstName = String type LastName = String type NamePair = (FirstName, LastName) newtype Name = Name NamePair
Observe above snippet, the type Name has
exactly one constructor and one fields NamePair inside it.
A newtype
declaration may use field-naming syntax, though of course there may only be one
field. Thus:
newtype Name =
Name {myName :: NamePair}
Why newtype?
Suppose you
want to define a new type, where the representation is same as existing one,
but has some variation, then you can go for newtype.
For example,
you can define PositveInteger, NegativeIntegers like below.
newtype
PositiveInteger = PositiveInteger Integer
newtype
NegativeInteger = NegativeInteger Integer
Above
definitions creates completely two new types:
PositiveInteger,
to represent positive numbers.
NegativeInteger,
to represent negative numbers.
Sample.hs
newtype PositiveInteger = PositiveInteger Integer deriving Show toInteger :: PositiveInteger -> Integer toInteger (PositiveInteger i) = i toPositiveInteger :: Integer -> PositiveInteger toPositiveInteger x | (x < 0) = error "Not applicable to negative numbers" | otherwise = PositiveInteger x
Prelude> :load Sample.hs [1 of 1] Compiling Main ( Sample.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> toInteger (PositiveInteger 10) <interactive>:4:1: Ambiguous occurrence ‘toInteger’ It could refer to either ‘Main.toInteger’, defined at Sample.hs:4:1 or ‘Prelude.toInteger’, imported from ‘Prelude’ at Sample.hs:1:1 (and originally defined in ‘GHC.Real’)
Oops, got an error. Observe above error
message, it is telling, toInteger function is defined in two places, one is in
Sample.hs and other is in Prelude.hs, So we should call like Main.toInteger.
*Main> Main.toInteger (PositiveInteger 10) 10 *Main> toPositiveInteger 10 PositiveInteger 10 *Main> *Main> toPositiveInteger (-10) PositiveInteger *** Exception: Not applicable to negative numbers
Can I perform Arithmetic operations on
PositiveInteger?
Let me try some
experiment.
*Main> PositiveInteger 10 + PositiveInteger 20 <interactive>:15:20: No instance for (Num PositiveInteger) arising from a use of ‘+’ In the expression: PositiveInteger 10 + PositiveInteger 20 In an equation for ‘it’: it = PositiveInteger 10 + PositiveInteger 20
Oops, I got an error again. Let me look
into it. It is clearly telling “No
instance for (Num PositiveInteger)”. To solve this, I need to give an instance
definition for PositiveInteger to the Num class.
Sample.hs
newtype PositiveInteger = PositiveInteger Integer deriving Show fromPositiveInteger :: PositiveInteger -> Integer fromPositiveInteger (PositiveInteger i) = i toPositiveInteger :: Integer -> PositiveInteger toPositiveInteger x | (x < 0) = error "Not applicable to negative numbers" | otherwise = PositiveInteger x instance Num PositiveInteger where fromInteger = toPositiveInteger x + y = toPositiveInteger (fromPositiveInteger x + fromPositiveInteger y) x - y = let r = fromPositiveInteger x - fromPositiveInteger y in if r < 0 then error "Unnatural subtraction" else toPositiveInteger r x * y = toPositiveInteger (fromPositiveInteger x * fromPositiveInteger y)
*Main> PositiveInteger 10 + PositiveInteger 20 PositiveInteger 30 *Main> *Main> PositiveInteger 100 * PositiveInteger 19 PositiveInteger 1900
What
is the difference between newtype and data?
a. First of all newtype has exactly one
constructor and one field inside it, where as the data type defined using data
keyword can have any number of value constructors with any number of fields.
b. Let me try to show an example.
Sample.hs
data MyData = MyData Bool getVal (MyData _) = "Hello Haskell"
Try
to call getVal by passing undefined as argument.
Prelude> :load Sample.hs [1 of 1] Compiling Main ( Sample.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> getVal undefined "*** Exception: Prelude.undefined
Oops, why I got this exception. It is
because, A type defined using data keyword, can have multiple value
constructors, so Haskell has to evaluate the expression undefined (‘getVal undefined’), when haskell tries to
evaluate undefined, error is thrown.
Now update
Sample.hs like below.
Sample.hs
newtype MyData = MyData Bool getVal (MyData _) = "Hello Haskell"
Prelude> :load Sample.hs [1 of 1] Compiling Main ( Sample.hs, interpreted ) Ok, modules loaded: Main. *Main> *Main> getVal undefined "Hello Haskell"
Strange isn’t it? this time we don’t get
any exception. Haskell knows that types made with the newtype keyword can only
have one constructor, it doesn't have to evaluate the value passed to the
function to make sure that it conforms to the (MyData _) pattern because
newtype types can only have one possible value constructor and one field. So
Haskell don’t need to evaluate undefined.
No comments:
Post a Comment