A rational number is a pair of integers of the form: (num, den) where den > 0 and gcd(num, den) = 1. type Rat = (Int, Int) Often type definitions are accompanied by selectors and constructors: num:: Rat->Int num (n, d) = n den:: Rat->Int den (n, d) = d Our constructor shows how local definitions are introduced: makeRat:: Int->Int->Rat makeRat n 0 = error "numerator can't be 0" makeRat n d = (n1, d1) where n1 = n `div` g d1 = d `div` g g = gcd n d > type Rat = String->Integer > makeRat num den = dispatcher > where > num1 = num `div`d > den1 = den `div`d > d = gcd num den > dispatcher msg > | (msg == "getNum") = num1 > | (msg == "getDen") = den1 > | otherwise = error("unrecognized message: " ++ msg) > num rat = rat("getNum") > den rat = rat("getDen") Of course we can also define constants: > rat1:: Rat > rat1 = makeRat 3 4 > rat2:: Rat > rat2 = makeRat 6 10 Once selectors and constructors have been defined, all subsequent definitions can be based on them: > add:: Rat->Rat->Rat > add r1 r2 > = makeRat a b > where > b = den(r1) * den(r2) > a = num(r1) * den(r2) + num(r2) * den(r1) > mul:: Rat->Rat->Rat > mul r1 r2 > = makeRat a b > where > a = num r1 * num r2 > b = den r1 * den r2 This definitions show how numbers can be converted to strings: > rat2str:: Rat->String > rat2str rat = show (num(rat)) ++ "/" ++ show (den(rat)) > rat3:: Rat > rat3 = add rat1 rat2 rat2str (n, d) = show(n) ++ "/" ++ show(d)