Modeling multiple inheritance

Brandon Michael Moore brandon at its.caltech.edu
Fri Sep 26 13:57:04 EDT 2003


On Thu, 25 Sep 2003 oleg at pobox.com wrote:
>
> 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.

That should solve my problem. Putting all the superclasses in a tuple
should work. I'm worried about large class hierarchies. If it works on the
java.* classes I should be fine. Have you used this approach before? I'm
worried about compile time, runtime costs from the casts (hopefully they
compile out), and maybe exceeding maximum stack depth in context
reduction. This is a clever solution. I like it. Now, is anyone up to
encoding the Dylan MRO in Haskell type classes? ;)

> 	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.

What type heap? It sounds like you are talking about information from an
OO runtime, or are you talking about the collection of instances. I tried
a system where method names were also represented by data types, but
without your solution for multiple inheritance I couldn't get the
implementation inheritance I wanted. How would you implement this dispatch
table? What are the advantages of this approach over the type class
encoding? I'm worried that generating bindings would be a problem if the
dispatch table needs to be a monolithic value with a very interesting type
in some file.

Brandon

> 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