Type aliases can be used to give other names to a given type. This way type signatures can document the code much better.
-- Without type aliases.
data Person = Person String Int
-- Using type aliases.
type Name = String
type Age = Int
data Person = Person Name Age
Another usage might be to give a short name to long types if they are used in many places.
type StrangeTable = [(Id, Either Person Vehicle)]
The newtype keyword can be used to create new types. It is similar to data at first sight.
data Name = Name String
-- identical declaration with newtype
newtype Name = Name String
In fact every newtype declaration can be replaced with data. However data declarations can only be replaced with newtype if they have only one constructor with exactly one field.
Why do we need it if we can express every newtype with data? Types declared with data have runtime overhead compared to types declared with newtype. The extra information supplied by newtype is completely removed after type checking. At runtime the bare value of the single field is used without any boxing or annotation.
Why don't we create a simple type alias to the type of the single field? Type aliases do not create a new type just create a new name that can be used to refer to the same type. That is a huge difference. Consider the following example.
-- using type aliases
type EncryptedData = String
sendToNetwork :: EncryptedData -> IO ()
-- this will compile and run since a String is the same
-- as EncryptedData
sendToNetwork "a simple string without encryption"
-- using a new type
newtype EncryptedData = EncryptedData String
sendToNetwork :: EncryptedData -> IO ()
-- this will not compile since String is not the same
-- type as EncryptedData
sendToNetwork "a simple string without encryption"
Using a separate type instead of just aliasing one will add type safety where it makes sense.
Creating a new type lets us define multiple instances of a typeclass.
class DefaultValue a where
value :: a
instance DefaultValue Int where
value = 10
-- we can implement multiple instances by wrapping Int in a new type
newtype AnotherInt = AnotherInt {getInt :: Int} deriving Show
instance DefaultValue AnotherInt where
value = AnotherInt 20
Try out the separate implementations in ghci:
> value :: Int
10
> getInt value :: Int
20
- Implement at least two instances of Double for the Pretty typeclass.
class Pretty a where
pretty :: a -> String