BEGIN 16_10_13_UserDefinedClasses.lhs EXAMPLE OF A USER-DEFINED CLASS: CLASS "VISIBLE" AND ITS INSTANCES > module Visible where > class Visible a where -- "a" is a type variable which can > toString :: a -> String -- be instantiated to any type > size :: a -> Int > instance Visible Bool where > toString True = "True" > toString False = "False" > size _ = 1 > instance Visible Char where > toString ch = [ch] > size _ = 1 > instance Visible Float where > toString x = show x > size x = truncate (log x) In the preceding, try "log2 x" instead of "truncate (log x)" for a different implementation of "size". When you call "toString" and "size" on a number of type "Float", insert explicitly the type as in, e.g., "size (1325.25 :: Float)". The function "log" is in the Haskell library. "log x" computes the natural logarithm of a number x, i.e., the logarithm to the base e of x. If we want the logarithm to the base 2, we need to program the function ourselves, as shown next. Here "log2 x" computes "1 + largest power of 2 <= x". > log2 x = 1 + help (truncate x) > where > help 1 = 0 > help n = 1 + help (n `div` 2) > instance Visible a => Visible [a] where > toString [] = "" > toString [x] = toString x > toString (x : xs) = (toString x) ++ "," ++ (toString xs) > -- toString lst = concat (map toString lst) > -- preceding line will not insert a comma > -- between two consecutive entries in lst > size lst = foldr (+) 1 (map size lst) When you call "toString" and "size" on a list of type "Float", insert explicitly the type as in, e.g., size [1000, 1325.25 :: Float] or size ([1000, 1325.25] :: [Float]) To make "Int" to be an instance of the class "Visible" is a little more complicated, because the argument of "log" has to be a type in the class "Floating": > instance Visible Int where > toString n = show n > -- for a cleaner version of "toString", try the following: > -- toString n = " " ++ (show n) ++ " " > size n = truncate(log (intToFloat n)) > where > intToFloat :: Int -> Float > intToFloat n = fromInteger (toInteger n) > -- for a different definition of "size" you can try: "log2 (intToFloat n)" > -- instead of "truncate(log (intToFloat n))" When you call "toString" and "size" on a number of type "Int", insert explicitly the type as in, e.g., "size (30000 :: Int)". Next is a polymorphic definition of so-called insertion-sort: > iSort :: Ord a => [a] -> [a] > iSort [] = [] > iSort (x : xs) = ins x (iSort xs) > where > ins x [] = [x] > ins x (y : ys) > | x <= y = x : (y : ys) > | otherwise = y : (ins x ys) The following function vSort is restricted to argument of type "[a]" where "a" must be in both pre-defined class "Ord" and user-defined class "Visible": > vSort :: (Ord a, Visible a) => [a] -> String > vSort = toString . iSort When you call "vSort" on a list of type "Float" or "Int", make sure to disambiguate the type of the list by explicitly turning it into type [Int] or [Float], as in vSort [2,5,2,3,4,5::Int] or vSort [2,5,2,3,4,5::Float] or vSort ([2,5,2,3,4,5]::[Int]) or vSort ([2,5,2,3,4,5]::[Float]) END 16_10_13_UserDefinedClasses.lhs