====== 6. Classes in Haskell ======
=== Default Classes ===
Today you can use the provided {{:pp:lab6_skel.zip|}}.
Last time we've learned how to define our new data types in Haskell. The simpler ones were ''Ordering'' (some "constants"), ''Point Float Float'', and ''Student String String [Float]''.
We've seen that we can define //recursively// //monomorphic// or //polymorphic// types, like ''List a'' and ''BTree a''.
Today we will use the following data types:
data List a = Null | Cons a (List a)
data BTree a = Void | Node a (BTree a) (BTree a)
data Student = Student {
first_name :: String,
last_name :: String,
grades :: [Float]
}
{--
For Student, we can define it as a 'struct Student' from C.
first_name will be a "getter" for our first String.
Haskell helps us by defining 3 simple functions for us to extract each member
Try :t Student and :t first_name in GHCI and you'll figure it out quickly.
--}
Now let's check one more interesting concept, Haskell's class system.
If you're defining a new //data Point//, you probably encountered some problems trying to print it in GHCI.
The quick fix would be adding the ''deriving Show'', which will print the data type in a 'default' manner (you can try).
But what exactly is this ''Show''? And how can we provide //our Show//?
We can look at the Show class in a simplified form:
class Show a where
show :: a -> String
Here ''a'' is a **data type**. More precisely, to be able to print any data type from Haskell, then it must implement the //show// function. That's why we can consider it an //interface//.
Providing our show will be done by enrolling our type in the already existing class Show. Here you have a simple example, in the lab skel you'll see a more complex show, //just for flexing//.
-- the type encapsulated in BTree must also be "showable", so we can do (show v)
instance (Show a) => Show (BTree a) where
show Void = ""
show (Node v l r) = "<"++(show l)++(show v)++(show r)++">"
1. Add List and Student to the Show class. You can print them however you want. If you aren't inspired today, you can use the following:
* The lists can be the default style -> //[3,4,1,2,]//
* The student can be something like -> //Studentul: ANDREI Alex-Bogdan = [8.5,6.0,8.7]//
2. The default ''=='' that we get from //deriving Eq// will check if 2 objects are identical. For our data, you'll have to provide a custom '==' such that:
* list1 == list2 = True if both trees have the same elements, but in any order
* tree1 == tree2 = True if both trees have the same elements, but in any order
* stud1 == stud2 = True if both students have the same average on their grades, same last_names and same first_names
3. We would like to use ''+'' and ''*'' on lists and trees to add/multiply corespondent elements. Enroll BTree and List in the ''Num'' class to access the + and * functions. If 2 lists or trees aren't at the same size, then we should consider unexisting corespondents as **Null/Void**. Also
* Void + Node = Node
* Void * Node = Void
* same for lists
4. Let's sort students now. Add the student to the ''Ord'' class and provide implementations for ''<''. The criteria will be their grades average, maximum grade, last_name, and first_name alphabetical. We will sort them by rankings. //stud1 < stud2// if //stud1// is better than //stud2// by the above criteria.
=== Custom Classes ===
What if we need to create our own classes? For a quick example, we would like a class that tells us if a data type is Empty or not. We will call this class ''IsVoid'' and all types enrolled in this class must implement the ''isVoid'' method.
class IsVoid a where
isVoid :: a -> Bool
instance IsVoid Bool where
isVoid False = True
isVoid True = False
instance IsVoid (BTree a) where
isVoid Void = True
isVoid _ = False
instance IsVoid [a] where
isVoid [] = True
isVoid _ = False
As you can see, now we can add our data types in the new class, but also Haskell's types. The only requirement is that the enrolled type must implement our method.
5. Create a class ''Contains b a'' that will require a ''contains :: b -> a -> Bool'' method which will return True if ''a'' is in ''b''. Add ''[a]'', ''List a'' and ''BTree a'' to this class.
Do we need any additional restrictions for a or b? You can still add restrictions:
class (SomeClass a) => Contains b a where ...
6. Create the ''class Size a'' which will require the methods ''size'' and ''uniqueSize''. Add ''[a]'', ''List a'' and ''BTree a'' to this class.
- //size// = the numbers of elements in **a**
- //uniqueSize// = the number of uniqueElements in **a**