BEGIN 16_10_13_LibraryClasses.lhs Good Reading on this topic: LIBRARY (i.e. PRE-DEFINED as opposed to USER-DEFINED) CLASSES: Sections 13.2, 13.3, 13.4 in "Haskell, the Craft of Functional Programming" by Simon Thompson, 3rd Edition. The general format of a class definition is: class where <... signature involving ...> <... optional definitions of names in the signature ...> A "signature" means a list of names and their types. If you are familiar with Java, this resembles the notion of "abstract class" rather than the notion of an "interface" -- because it optionally allows the inclusion of definitions of some (or all) of the names in the signatures. The purpose of the optional definitions in the class is to make them default in all instances of the class. If the user so wishes, these default definitions can be overridden in an instance. For some of the examples below, we want to override some of the definitions in Prelude.hs, so we insert the following "import": > import Prelude > hiding (Eq (..), Ord (..)) > -- we hide the classes Eq and Ord, > -- and all of their functions, which is why we > -- insert "(..)" after each one of them. BEGIN EXAMPLE: Two Pre-Defined Classes: Eq, Ord -- each of which uses one type variable "a" in its heading These two classes are library classes, i.e. built-in or pre-defined, but we have "hidden" them when we imported Prelude.hs, so that we can copy their definitions here. The most ubiquitous class is the pre-defined class Eq, which is declared as follows: > class Eq a where -- "a" is a type variable which can > -- be instantiated to any type > (==) :: a -> a -> Bool -- first signature name > (/=) :: a -> a -> Bool -- second signature name > (==) x y = not (x /= y) -- first default definition > (/=) x y = not (x == y) -- second default definition Because of the relationship between (==) and (/=), which is specified in the default definitions, it suffices to define only one of the two in an instance of Eq. There is an apparent circularity in the definitions of the functions (==) and (/=) above, since they say: the default definition of (/=) depends on the definition of (==) being available, and the default definition of (==) depends on the definition of (/=) being available. But this circularity only means that, for a type to be declared an instance of the class Eq, only one of the two required functions needs to be defined, with the other implicitly defined by default. For example, Int is defined to be an instance of Eq as follows: > -- instance Eq Int where (==) = primEqInt > -- defined by means of primEqInt, which is a built-in > -- primitive Haskell function on integers > instance Eq Bool where > True == True = True > False == False = True > _ == _ = False > -- defined by pattern matching Another pre-defined class which is almost as ubiquitous as "Eq" is "Ord": > class Eq a => Ord a where -- "a" is a type variable which can > -- be instantiated to any type provided > -- it is in the class "Eq", as expressed > -- by the context "Eq a =>" > (<) :: a -> a -> Bool > (<=) :: a -> a -> Bool > (>) :: a -> a -> Bool > (>=) :: a -> a -> Bool > max :: a -> a -> a > min :: a -> a -> a > compare :: a -> a -> Ordering -- "Ordering" is a predefined enumerated > -- type LT | EQ | GT > compare x y > | x == y = EQ > | x <= y = LT > | otherwise = GT > (<=) x y = (compare x y) /= GT -- (<=) depends on compare, and > (<) x y = (compare x y) == LT -- compare depends on (<=), why is > (>=) x y = (compare x y) /= LT -- this not a problem? > (>) x y = (compare x y) == GT > max x y > | x >= y = x > | otherwise = y > min x y > | x <= y = x > | otherwise = y In the preceding declaration of "Ord" we compare members of "Ordering" using equality, so we need to make "Ordering" an instance of "Eq": > instance Eq Ordering where > (==) LT LT = True > (==) EQ EQ = True > (==) GT GT = True > (==) _ _ = False END EXAMPLE END 16_10_13_LibraryClasses.lhs