Modeling multiple inheritance
oleg at pobox.com
oleg at pobox.com
Thu Sep 25 20:20:02 EDT 2003
Brandon Michael Moore wrote:
> So I defined a class to model the inheritance relationships
> class SubType super sub | sub -> super where
> upCast :: sub -> super
> Now I can define a default instance of HasFooMethod:
> instance (HasFooMethod super args result,
> SubClass super sub) =>
> HasFooMethod sub args result where
> foo sub args = foo (upCast sub) args
> This will propagate foo methods down the inheritance hierarcy. If a new
> class C is derived from A, I just need to say
> One problem is that the subclass relationship needs the functional
> dependency
> Does anyone know of clever solutions that would model multiple inheritance
> while preserving the functional dependencies (unsafe compiler flags are
> fine too), or ways to reduce the pain of overloading resolution without
> the functional dependency?
Yes. The code included. The solution is trivial: in case of a multiple
inheritance, a class has a _sequence_ of superclasses rather than a
single superclass. Like
instance SubClass (Object,()) ClassA
instance SubClass (Object,()) ClassB
-- Multiple inheritance (including the diamond!)
instance SubClass (ClassA,(ClassB,())) ClassC
instance SubClass (ClassA,(ClassB,(ClassC,()))) ClassD
And we need some intelligence to traverse the sequence. But even a
computer can do that.
I would like to propose a different solution: a dual of
typeclasses in the value domain. Function foo is just a regular
function
foo:: Object -> Int -> Int
foo x y = y
We then need a class MApplicable fn args result with a method
mapply. The trick is that the method should take any object of a type
castable and cast it to the type of the first argument of fn. The cast
can be made safe and statically checkable, using the type
heap. Actually, we can use the type heap to model the dispatch table
(whose rows are functions and columns are object/classes). Given a
function and an object, we can search in many way for the applicable
combination.
And now, the code for the solution that works.
Compiler flags:
-fglasgow-exts -fallow-overlapping-instances -fallow-undecidable-instances
data Object = Object
data ClassA = ClassA
data ClassB = ClassB
data ClassC = ClassC
data ClassD = ClassD
class SubClass super sub | sub -> super where
upCast :: sub -> super
instance SubClass (Object,()) ClassA
instance SubClass (Object,()) ClassB
-- Multiple inheritance (including the diamond!)
instance SubClass (ClassA,(ClassB,())) ClassC
instance SubClass (ClassA,(ClassB,(ClassC,()))) ClassD
class HasFooMethod cls args result where
foo :: cls -> args -> result
instance (SubClass supers sub,
HasFooMethod supers args result)
=> HasFooMethod sub args result where
foo obj args = foo (upCast obj) args
instance (HasFooMethod cls args result) => HasFooMethod (cls,()) args result
where
foo (x,()) = foo x
instance (HasFooMethod cls args result) => HasFooMethod (x,cls) args result
where
foo (x,y) = foo y
instance HasFooMethod Object Int Int where
foo _ x = x
test1::Int = foo Object (1::Int)
test2::Int = foo ClassA (2::Int)
test3::Int = foo ClassD (3::Int)
-- Likewise for another method:
class HasBarMethod cls args result where
bar :: cls -> args -> result
instance (SubClass supers sub,
HasBarMethod supers args result)
=> HasBarMethod sub args result where
bar obj args = bar (upCast obj) args
instance (HasBarMethod cls args result) => HasBarMethod (cls,()) args result
where
bar (x,()) = bar x
instance (HasBarMethod cls args result) => HasBarMethod (x,cls) args result
where
bar (x,y) = bar y
instance HasBarMethod ClassB Bool Bool where
bar _ x = x
test4::Bool = bar ClassB True
test5::Bool = bar ClassC True
test6::Bool = bar ClassD True
More information about the Haskell-Cafe
mailing list