This is an old revision of the document!
6. Classes in Haskell
Default Classes
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:
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 data can be considered the class from Java, then class
could be considered the interface from Java.
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. More precisely, to be able to print any data 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
. 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
- size = the numbers of elements in a
- uniqueSize = the number of uniqueElements in a