Here's a thought, engendered by a glass of beer in the Lamplighter's pub
in Gastown, Vancouver by John Hughes, Lennart Augustsson, and Simon PJ:
type classes can model extensible records.
Suppose we want to define a record for a person:
record Person = { Age : Int; Weight : Int }
and want to extend it to describe a Haskell programmer
record HaskellProgrammer = Person + { LinesPerDay : Int }
We'd like any function which took a Person as an argument to
also be able to take a HaskellProgrammer. Thus:
isOld :: Person -> Bool
makeSlimmer :: Person -> Person
To do this in Haskell, one could define Person and HaskellProgrammer
as classes (rather than types), thus:
class Person a where
setAge :: a -> Int -> a
getAge :: a -> Int
...similarly for Weight...
class Person a => HaskellProgrammer a where
setLinesPerDay :: a -> Int -> a
getLinesPerDay :: a -> Int
Notice that HaskellProgrammer has Person as a superclass.
Functions over records become overloaded:
isOld :: Person a => a -> Bool
makeSlimmer :: Person a => a -> a
That's about it really. Of course, one might want some
syntactic sugar for record selection etc, but the point is that the
type system handles the inheritance aspects quite neatly. You get
multiple inheritance too:
class (Person a, Location a) => LocatedPerson a
Here a LocatedPerson has all the fields of a Person and a Location.
There's some runtime overhead, that of passing dictionaries. That's
because representation decisions can be made independently:
data PersonRec = PersonRec Int Int
instance Person PersonRec where
setAge (PersonRec age weight) new_age = PersonRec new_age weight
...and so on...
You can choose among various representations for HaskellProgrammer:
data HaskellProgrammerRec = HaskellProgrammerRec Int Int Int
instance Person HaskellProgrammerRec where
setAge (...) new_age = ...
...etc...
instance HaskellProgrammer HaskellProgrammerRec where
setLinesPerDay (...) new_lpd = ...
...etc...
or
data HaskellProgrammerRec = HaskellProgrammerRec Int PersonRec
instance Person HaskellProgrammerRec where
setAge (HaskellProgrammerRec lpd person) new_age
= HaskellProgrammerRec lpd (setAge person new_age)
...etc...
...etc...
You can even have more than one representation in use at a time.
Indeed, if we allow these "record classes" to have methods other
than simply field selection, the whole thing begins to have a rather
object-oriented flavour. (Method override is lacking, but we have inheritance
and a mixture of "data" and "method" fields.)
This isn't necessarily the best way to introduce records or OOP into Haskell,
but it is an example of what you can do with type classes.
Simon, John, Lennart.