**Day 1, April 17, 2020: Typeclasses**

*Typeclasses are among the most powerful features in Haskell. They allow us to define generic interfaces that provide a common feature set over a wide variety of types; defining * *a set of functions that can have different implementations depending on the type of data they are given.*

Here is a typeclass is defined:

class BasicEq a where isEqual :: a -> a -> Bool isEqual x y = not (isNotEqual3 x y) isNotEqual :: a -> a -> Bool isNotEqual x y = not (isEqual x y)

and here is how it is used (instantiated):

instance BasicEq Bool where isEqual True True = True isEqual False False = True isEqual _ _ = False

and also

instance BasicEq Color where isEqual Red Red = True isEqual Green Green = True isEqual Blue Blue = True isEqual _ _ = False

This is like `refl`

in Martin-Löf type theory. If `BasicEq`

or `==`

is not implemented for a type, you could say (in the abstract) that that type has no notion of equality since `=`

is uninhabited.) You can instantiate an existing typeclass for a new type:

instance Show Color where show Red = "Red" show Green = "Green" show Blue = "Blue"

On the `Read`

and `Show`

typeclasses:

*You may often have a data structure in memory that you need to store on disk for later retrieval or to send across the network. The process of converting data in memory to a flat series of bits for storage is called serialization. It turns out that read and show make excellent tools for serialization. show produces output that is both human- and machine-readable. Most show output is also syntactically valid Haskell, though it is up to people that write Show instances to make it so.*

**Example**

First write a data structure to disk:

ghci> let d1 = [Just 5, Nothing, Nothing, Just 8]::[Maybe Int] ghci> putStrLn (show d1) [Just 5,Nothing,Nothing,Just 8] ghci> writeFile "test" (show d1)

Now read it back:

ghci> input <- readFile "test" "[Just 5,Nothing,Nothing,Just 8]" ghci> let d2 = read input Ambiguous type variable `a' in the constraint: `Read a' arising from a use of `read' at <interactive>:1:9-18 Probable fix: add a type signature that fixes these type variable(s) ghci> let d2 = (read input)::[Maybe Int] ghci> print d1 [Just 5,Nothing,Nothing,Just 8] ghci> print d2 [Just 5,Nothing,Nothing,Just 8] ghci> d1 == d2 True

**Automatic derivation**

data Color = Red | Green | Blue deriving (Read, Show, Eq, Ord)

**Pragmas**

{-# LANGUAGE TypeSynonymInstances #-}

**Data versus Newtype**

data DataInt = D Int deriving (Eq, Ord, Show) newtype NewtypeInt = N Int deriving (Eq, Ord, Show)

When we declare a newtype, we must choose which of the underlying type’s typeclass instances we want to expose. 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: ghci> N 1 < N 2 True

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:

ghci> N 313 + N 37 --> error

Further example (hiding implementation):

newtype UniqueID = UniqueID Int deriving (Eq)

The compiler treats UniqueID as a different type from Int. As a user of UniqueID, we know only that we have a unique identifier; we cannot see that it is implemented as an Int.

**Recap: defining types**

- The
`data`

keyword introduces a truly new algebraic data type. - The
`type`

keyword gives us a synonym to use for an existing type. We can use the type and its synonym interchangeably. - The
`newtype`

keyword gives an existing type a distinct identity. The original type and the new type are not interchangeable.

.