
On Tue, 12 Oct 2004, John Goerzen wrote:
One of the best features of OO programming is that of inheritance. It can be used to slightly alter the behavior of objects without requiring modification to existing code or breaking compatibility with existing APIs.
I hesitate to express a contrary opinion since it'll sound as though I'm defending Haskell's limitations, but that's actually not the case here -- this was true even before I learned Haskell. In my own OOP code (mainly C++) I've rarely used implementation inheritance, and when I have I've never felt entirely happy about the way it turned out; it always seemed a bit fragile and hacky. When I want to take advantage of polymorphism I usually use abstract interfaces, and when I want to share code I usually use containment and delegation, which has always struck me as more robust. In any case, Haskell does support polymorphic abstract interfaces and containment and delegation. In your ConfigParser example you would have an interface (say IConfigParser) which would be represented as a type class, and two implementations (ConfigParser and EnvConfigParser) which would be represented as instances of the type class. E.g. class IConfigParser a where newConfigParser :: IO a readConfigFile :: a -> FilePath -> IO () getFoo :: a -> IO Foo setFoo :: a -> Foo -> IO () ... data ConfigParser = ... instance IConfigParser ConfigParser where ... data EnvConfigParser = ECP ConfigParser instance IConfigParser EnvConfigParser where newConfigParser = liftM ECP newConfigParser readConfigFile (ECP cp) path = readConfigFile cp path >> envSubst cp getFoo (ECP cp) = getFoo cp ... I should say, though, that this is very unidiomatic code. Partly this is because I don't quite understand the meaning of your ConfigParser class -- does it exist before a configuration file is read? What is the meaning of having more than one instance? Parsing configuration files strikes me as more verb than noun, and I'd be more inclined in this case to declare a single ConfigData type, a single function to write it to a file, and two functions to read it, one with environment substitution and one without. So I suppose my advice is twofold: 1. Try replacing implementation inheritance with containment and delegation when you translate to Haskell. 2. Try revisiting the original problem and thinking about how to solve it in a Haskellish way, rather than solving it in another language and then translating. -- Ben