|
|
Subject: Modeling multiple inheritance - msg#00112
List: lang.haskell.cafe
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
Was this page helpful?
Thread at a glance:
Previous Message by Date:
click to view message preview
Re: lexer puzzle
Note I've replied to haskell-cafe. This post is a bit chatty and low on
solid answers.
On Thu, 25 Sep 2003, Sean L. Palmer wrote:
> > >>A... should be split into "A.." and "."
> > >I found a compromise: let's make it a lexing error! :-)
> > At least that agrees with what some Haskell compilers implement. No
> > current Haskell compiler/interpreter agrees with what the report seems
> > to say, that is that "A..." should be lexed as the two tokens "A.." and
> > ".", and similarly, "A.where" should be lexed as "A.wher" followed by "e".
>
> Hi. I'm really new to Haskell, just learning it, and I must say I'm pretty
> overwhelmed by the large variety of constructs. (=>, <-, \ to name a few)
Would that be \ as in TREX row variable polymorphism? Just remember most
operators are just library functions. It's only =, ->, =>, <-, :: that are
really part of the language, and {,},; for grouping. Did I miss any?
>
> But I'm just writing this to let you guys know (surely you know this
> already) that anyone from a C/C++/Java/Delphi background is going to
> completely misunderstand the meaning of A.anything in Haskell... it's
> completely nonintuitive to people with my background. I kinda like dot
> notation because it ties together the symbols visually, for instance
> "myrec.myfield" is more of a unit than "myrec myfield". It stays together
> better when surrounded by other code, and would result in fewer parenthesis
> necessary.
A Python programmer would understand instantly: Python uses exactly the
same syntax for module access, though Python modules are usually in
lowercase. It also seems to be very much in the spirit of "access a member
of this object" of an OO language.
Or was that supposed to be composition of a constructor with a function, A
. f? Function composition, and higher order functions in general are
likely to confuse an imperative programmer, but I think there isn't much
syntax can do there.
Or are you talking about the field access syntax? Maybe the problem is
that dot has two to five different meanings, function composition, naming
module members, building hierarchial module names, being a decimal point,
and making elipses, and is commonly used for yet another purpose in OO
languages.
> Haskell to me seems to be a great language with a syntax problem, and a bad
> case of too many ways to do the same thing; thus every programmer does
> things their own way and it's difficult to grasp the language by looking at
> various programs, since they're all so very different. As a small example,
> there's 'let' vs. 'where'. Maybe a bit of pruning would be in order.
Do you mean the syntax is bad in places? Haskell is the cleanest language
I know of, but I'm sure it has some grungy bits. I've had problems with
unary minus (can't slice binary minus), and precedence of with irrefuatble
patterns and type ascription. I would be happy for any confusing syntax to
be improved. Any good ideas? Syntax change is a possibility: do notation
is a relatively recent addition, and arrow syntax is in the works.
I think you might instead mean the syntax cuts down our market share
because it isn't like common (C derived) languages. I don't think Haskell
could borrow any more syntax from C without actually making the language
worse. It's a problem, but not with the syntax. If someone is so solidly
into a C++/Java/OO mindset that the syntax would be a problem, the
semantics would probably be even more of a problem.
I would suggest Python if Haskell was too much of a jump for someone. It's
still OO, but it encourages more flexible and interesting programs, and
you don't have to live in a Java type system. Plus, it has more libraries,
bindings, and PR, so it's easier to get permission to use it in a company.
If someone is used to Python's layout rule and lack of type signatures,
and gets their head around some of the fun you can have dynamically
picking which members of an object to access, assigning to __dict__ and so
on, then Haskell should be much less of a jump in syntax, and less
imposing in semantics.
> That said, I still think it looks more promising than any other language
> I've looked at that actually is being actively used and maintained and has a
> decent installed base and good cross platform support. So I will learn it.
> I just wish the transition was easier and that it took less time to learn.
> ;)
>
> Sean
I learned Haskell from the "gentle introduction". It seemed gentle enough
to me but others disagree, so I'm probably not the best for advice for the
raw beginner. If you are interested in learning about monads though,
Jeff Newbern's monad tutorial seems accessible and as complete as anything
this side of Phil Wadler's paper.
I hope learning Haskell goes well.
Brandon
Next Message by Date:
click to view message preview
Re: lexer puzzle
Brandon Michael Moore <brandon@xxxxxxxxxxxxxxx> writes:
> Or was that supposed to be composition of a constructor with a function, A
> . f? Function composition, and higher order functions in general are
> likely to confuse an imperative programmer, but I think there isn't much
> syntax can do there.
I think there is a problem with too much overloaded syntax. Perhaps
it is time to put non-ASCII characters to good use?
For instance, function composition could use the degree sign: °
and leave the . for module qualification.
Template Haskell could use double-angle quotation marks: « »
and the section sign: §
and avoid clashing with list comprehensions and the function
application operator.
Implicit parameters could use an inverted question mark: ¿
And so on, just look for places where the semantics depend on spaces
in the right (or wrong) place.
-kzm
--
If I haven't seen further, it is by standing in the footprints of giants
Previous Message by Thread:
click to view message preview
Re: Modeling multiple inheritance
On Thu, 25 Sep 2003 ozone@xxxxxxxxxxxxxxxx wrote:
> On 25/09/2003, at 7:22 AM, Brandon Michael Moore wrote:
>
> > I'm trying to build a nicer interface over the one generated by
> > jvm-bridge. I'm using fancy type classes to remove the need to mangle
> > method names. I would like methods to be automatcially inherited,
> > following an inheritance hierarcy defined with another set of type
> > classes.
> ...
>
> Hi Brandon, it looks like the way that you're modelling inheritance and
> OO-style overloading is basically the same way that I did in my thesis:
>
> http://www.algorithm.com.au/mocha
>
> The actual implementation of the thesis will be up in CVS in ~24 hours,
> I'm just waiting from an email back from the people I'm getting it
> hosted with.
>
> If you want a quick run-down on how I did the OO-style overloading
> without delving into the paper, let me know and I'll post a quick
> summary. I've only skimmed your email, but I think that the problem
> you're having with interfaces is solved with the way I'm modelling OO
> overloading and class inheritance.
Thanks. I think I could use the summary. I already found and skimmed your
thesis, and I don't think it gives me exactly what I want. All you do in
chapter 3 is represent a multiple inheritance hierarcy. I want default
instances that will propagate method definitions along the hierarcy. I'm
not sure that's possible though.
I want something like this:
data Object
data ClassA
data ClassB
data ClassC
class SubClass super sub <???>
instance SubClass Object ClassA
instance SubClass Object ClassB
instance SubClass ClassA ClassC
instance SubClass ClassB ClassC
class HasFooMethod cls args result <??>
foo :: cls -> args -> result
instance SubClass super sub, HasFooMethod super args result ,???
=> HasFooMethod sub args result where
foo obj args = foo (upCast obj) args
instance HasFooMethod Object int int where
foo = id
(now all four classes have a foo method)
Brandon
Next Message by Thread:
click to view message preview
Re: Modeling multiple inheritance
On Thu, 25 Sep 2003 oleg@xxxxxxxxx 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
>
>
|
|