Next topic to learn in CIS 194 was type classes. They’re quite similar to interfaces in some other languages and used for similar purpose: polymorphism.
Previous lessons had introduced me to type classes already. For example, in order to be able to compare two values, they have to belong to Eq type class. Eq declares, that every type that is instance of it, has two functions defined for it == and /=. They are used to compare two instances for equality or inequality. Many standard types, like Integer and Char belong to this type class.
Eq can be defined as:
class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)
So, it even comes with default implementations for the two functions (that are recursive). This means that it’s enough to declare only == or /= and default implementation will take care of the rest.
If we have an imaginary type Foo, we can make instance of the type class as following:
data Foo = Foo Integer instance Eq Foo where (Foo a) == (Foo b) = a == b
The exercise was interesting. It evolved through multiple stages (as usual) where you had to implement a simple calculator. First a very concrete implementation with simple integers and then one using type class that you defined. After that there were series of different kinds of calculators using the type class, culminating with a hypothetical compiler for stack based calculator. I liked doing the exercise, as it wasn’t too convoluted or made up and showed power of type classes nicely.
Like, the type class that I defined was simple (literals, addition and multiplication):
class Expr a where lit :: Integer -> a add :: a -> a -> a mul :: a -> a -> a
And instance for integers was easy enough:
instance Expr Integer where lit a = a add a b = a + b mul a b = a * b
But the neat trick was, that I could implement calculator using modulo 7 numbers (ie. no number was ever larger than 7), just by defining:
newtype Mod7 = Mod7 Integer deriving (Show, Eq) instance Expr Mod7 where lit a = Mod7 $ a `mod` 7 add (Mod7 a) (Mod7 b) = Mod7 $ (a + b) `mod` 7 mul (Mod7 a) (Mod7 b) = Mod7 $ (a * b) `mod` 7
And for stack based virtual machine:
instance Expr VM.Program where lit a = [VM.PushI a] add a b = a ++ b ++ [VM.Add] mul a b = a ++ b ++ [VM.Mul]
Pretty nifty, quite readable and very concise (without being obscure).