Skip to content

Latest commit

 

History

History
103 lines (76 loc) · 2.58 KB

29.md

File metadata and controls

103 lines (76 loc) · 2.58 KB

Type aliases

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)]

newtype

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

Exercise

  • Implement at least two instances of Double for the Pretty typeclass.
class Pretty a where
  pretty :: a -> String