www.digitalmars.com         C & C++   DMDScript  

D - Templates

reply Geoff <gpark ibm.net> writes:
Walter,

 I agree with you that multiple inheritance is of dubious usefulness.
Smalltalk and Java do just fine without it. I encounter multiple
inheritance a lot in C++ code, but it never makes the code more
understandable than the equivalent written with single inheritance. It's
important to remember that high level languages are for people to read,
not machines, else we'd write everything in asm.
So if you and I, and a lot of other experienced programmers find
designing code and/or reading code designed by others using multiple
inheritance gnarly, that's reason enough not to have it.

I've written significant amounts of code in C, C++, Smalltalk, Java,
Fortran, APL, Pascal, Cobol, and various macro assemblers. I've read
about others, including Ada and Eifel. They all have problems. C and
Smalltalk are probably my favorites. C for its simplicity and power.
Smalltalk  for its OO. Over the course of a 3 year, ten person C++
project, I have grown to detest C++.  If C++ was supposed to bring the
benefits of OO to C, it has failed utterly.

Maybe D can do it right. I like most of what you have in the spec.
Please see if you can find a way to leave templates out. Yes you need to
have generic code, but there has to be a better way. Most of the people
who have said they need templates, really just mean that they need
generic code.

Smalltalk, the original OO language, is still the language that allows
generic code to be written most easily.  Of course big  Smalltalk
programs have a nasty habit of falling over at runtime with a 'message
not understood by...' pop-up because an object passed to a generic
method doesn't implement a required interface.   Type checking can save
your butt.

It seems to me that you could have the simplicity of Smalltalk style
generic code, if only the compiler could forsee all possible types that
can arrive at a method. In Smalltalk you try to make your code more
robust by asking incoming parameters for their type info, before you try
to access a non-existant member. Of course, then your code isn't
completely generic anymore.

Interfaces, as in Java, help. Maybe all function parameters should be
typeless, but instead define only the interface(s) that each parameter
must implement. i.e.

foo( a { implements + operator),  b { implements iterator, + operator }
);

I'm not sure this is making sense, but give it some thought.

-Geoff
Aug 17 2001
parent reply "Glen Dayton" <dayton timesten.com> writes:
We've mixed two different topics.

Many OO programmers believe multiple inheritance to be of dubious value, but
perhaps this is because of the lack of expressiveness in the languages we
have been forced to use of late.  One of the arguments against multiple
inheritance is that in most OO languages a common subclass may always be
found, but this leads to over-abstraction and a violation of isolation
principles.  Simple mathematical analysis using set notation demonstrates
the need for multiple inheritance, and the limitations of our current
languages.  To omit multiple inheritance would gravely harm the utility of
the language just because we haven't thought out what is really meant by
multiple inheritance.

For example, an amphibous truck is both a truck and a boat.  Trucks and
boats are vehicles.  Unfortunately, in C++, the class tree that represents
this yields ambiguous definitions:

class Vehicle {...}
class Truck : Vehicle {...}
class Boat : Vehicle {...}
class AmphibiousTruck : Boat, Truck;

AmphibiousTruck inherits the union of attributes of Boat and Truck, but not
all Trucks are AmphibiousTrucks -- so it would not be correct to downcast
all references to Trucks to references of AmphibiousTrucks.  Never mind C++
introduces arcane "virtual" inheritance to control whether you get 2 sets of
Vehicle attributes or just 1.

Java interfaces don't help much.  An AmphibiousTruck is not merely a Vehicle
with a Truck interface and a Boat interface.  You can create instances of
Boats and Trucks but you can't create instances of interfaces.

One solution is that D implement multiple inheritance, but all inheritance
is statically "virtual".   C++ does virtual inheritance through high
overhead vtable pointers.  D has enough information in its type library
(from not having to use include files) to coalesce multiple references to
base classes.  I'm sure, though, that this approach has its own shortcomings
that I hope many will point out.

Template classes also capture the important mathematical concepts of the
transformations of sets.  They are absolutely essential if you want to get
rid of non-type safe macros.
Aug 21 2001
parent reply "Bradeeoh" <bradeeoh crosswinds.net> writes:
 Java interfaces don't help much.  An AmphibiousTruck is not merely a

 with a Truck interface and a Boat interface.  You can create instances of
 Boats and Trucks but you can't create instances of interfaces.

You're right, you can't create instances of Interfaces. But, indeed, you can refer to objects by the interfaces they implement. For example (using Java notation ) - public class AmphibiousTruck extends Vehicle implements Boat, Truck {} public class foo { private Boat boatTruck = new AmphibiousTruck(); private Truck truckBoat = new AmbhibiousTruck(); } Now, I'm not saying there aren't any cases where you can't do something with interfaces that you CAN do with multiple inheritence. I just don't know any. I cheerfully challenge you to show me one :) BTW - I have never run into that problem, perhaps, because of how I construct my hierarchies of objects. Ie - I would implement these examples more like this - public class Vehicle{} public class Truck extends Vehicle implements Wheeled, CargoCarrier, ManualDriver{} public class Boat extends Vehicle implements Floats, ManualDriver{} public class AmphibiousTruck extends Vehicle implements Wheeled, Floats, CargoCarrier, ManualDriver{} public class AmphibiousHoverTruck extends Vehicle implements Wheeled, Floats, CargoCarrier, ManualDriver, Hovers{} etc. ... .. There's a bazillion ways of doing it. It all depends on the problem. No way is more right or wrong than any other, as long as the philosophy behind the design is consistent throughout the project. Some solutions aren't appropriate for some situations, of course, but I contend that for any multiple inheritence solution that would be considered "appropriate", there's a single inhereitence + interface solution to match that alleviates alot of the multiple inhereitence problems. -Brady
Aug 21 2001
parent reply "Glen Dayton" <dayton timesten.com> writes:
Of course your arguments apply if you have control of the hierarchy; and in
fact you've spuriously introduced whole new classes and interfaces to
overcome a limitation of the language.  My specification said nothing about
"Wheeled", "CargoCarrier", and "ManualDriver", and so limits the
extensibility of the objects in that it now precludes tracked and
semi-tracked carriers.  The language should make expression of the solution
of the problem clear without having to resort to tricks such as wrapping
classes around interfaces so you can get an instance of them.

One of the frustrating aspects of Java is having to be conscious of what is
an interface and what is not.  Until you attempt to create a new instance
you don't know.  A simple test of whether a language is really extensible
and suitable for re-usable software is to see how complicated it is to
implement each one of the well-known design patterns, such as factory.  So,
why are multiple interfaces superior to multiple inheritance?

If you consider the _type_ of each attribute of a class as a set, then the
class becomes a cartesian product of the types of its attributes.  An
instance of the class is merely a member of the set of that cartesian
product.  The methods of a class merely become mappings, which may be, and
should be treated identically as attributes.  In terms of programming
practice for maintenance, we usually hide the attributes, and make the
mappings available.  In mathematical terms, though, there is no reason to
treat them differently.  When you start treating methods as members of sets,
there is no mathematical difference between interfaces and multiple
inheritance.

The C++ iostream class is a clear demonstration of when multiple inheritance
is useful and elegant. It is possible to use abstract classes and multiple
inheritance to simulate interfaces, but because you can't instantiate
interfaces, you can't use interfaces to simulate multiple inheritance.
(Don't confuse this type of interface with CORBA's instantiable interfaces).
In other words -- it was possible to implement iostreams with interfaces,
but the code would have been less extensible and more complicated.

If you really want D to be compiled-to-native-code Java, it already exists
(look at Apogee Software's offerings).  If D is really to be the successor
to C++, it really needs to drop Java concepts like "no multiple
inheritance".
Aug 22 2001
parent reply "kaffiene" <kaffiene xtra.co.nz> writes:
 One of the frustrating aspects of Java is having to be conscious of what

 an interface and what is not.  Until you attempt to create a new instance
 you don't know.  A simple test of whether a language is really extensible
 and suitable for re-usable software is to see how complicated it is to
 implement each one of the well-known design patterns, such as factory.

 why are multiple interfaces superior to multiple inheritance?

One of the good things about Java is having to be explicit about what is an interface and what isn't. It leads to good design rather than the sloppy way it is done implicitly in C++
 The C++ iostream class is a clear demonstration of when multiple

 is useful and elegant. It is possible to use abstract classes and multiple
 inheritance to simulate interfaces, but because you can't instantiate
 interfaces, you can't use interfaces to simulate multiple inheritance.
 (Don't confuse this type of interface with CORBA's instantiable

 In other words -- it was possible to implement iostreams with interfaces,
 but the code would have been less extensible and more complicated.

If you think that iostreams is an argument for Multiple inheritence then I think you're mad :-) Peter.
Aug 22 2001
parent reply Dan Hursh <hursh infonet.isl.net> writes:
kaffiene wrote:
 
 One of the frustrating aspects of Java is having to be conscious of what

 an interface and what is not.  Until you attempt to create a new instance
 you don't know.  A simple test of whether a language is really extensible
 and suitable for re-usable software is to see how complicated it is to
 implement each one of the well-known design patterns, such as factory.

 why are multiple interfaces superior to multiple inheritance?

One of the good things about Java is having to be explicit about what is an interface and what isn't. It leads to good design rather than the sloppy way it is done implicitly in C++
 The C++ iostream class is a clear demonstration of when multiple

 is useful and elegant. It is possible to use abstract classes and multiple
 inheritance to simulate interfaces, but because you can't instantiate
 interfaces, you can't use interfaces to simulate multiple inheritance.
 (Don't confuse this type of interface with CORBA's instantiable

 In other words -- it was possible to implement iostreams with interfaces,
 but the code would have been less extensible and more complicated.

If you think that iostreams is an argument for Multiple inheritence then I think you're mad :-) Peter.

I do think the truck and boat example was a good reason though. If someone already implement a truck and someone else implemented a boat, and I wanted to be able to make some that was both, I can't. Period. The closed I could come would either be to re write the world, or create an object he is one thing (a truck) and has the other (a boat) and access the has-a member when I need to be that type, and even then it isn't as flexible. Single inheritance may be easier to implement, but you are losing something. It's a little concerning how often folks here take the opinion that "Feature X has problems and I never use it anyway, so no body else 'really' needs it." I'm not specificly blaming you, but i've lost track of how many time if seen that reasoning tonight. I'm afraid I'll see it a lot in the 275 I still have to read. Dan
Aug 23 2001
next sibling parent reply "Walter" <walter digitalmars.com> writes:
Dan Hursh wrote in message <3B85F62A.3C3D8591 infonet.isl.net>...
 Single inheritance may be easier to implement, but you are losing
something.  It's a little concerning how often folks here take the
opinion that "Feature X has problems and I never use it anyway, so no
body else 'really' needs it."  I'm not specificly blaming you, but i've
lost track of how many time if seen that reasoning tonight.  I'm afraid
I'll see it a lot in the 275 I still have to read.

Your reasoning has merit. The counterargument (and I've discussed this at length with my colleagues) is that C++ gives you a dozen ways and styles to do X. Programmers tend to develop specific styles and do things in certain ways. This leads to one programmer's use of C++ to be radically different than another's, almost to the point where they are different languages. C++ is a huge language, and C++ programmers tend to learn particular "islands" in the language and not be too familiar with the rest of it. Hence one idea behind D is to *reduce* the number of ways X can be accomplished, and reduce the balkanization of programmer expertise. Then, one programmer's coding style will look more like another's, with the intended result that legacy D code will be more maintainable. For example, over the years I've seen dozens of different ways that debug code was inserted into a program, all very different. D has one way - with the debug attribute/statement. C++ has a dozen string classes plus the native C way of doing strings. D has one way of doing strings. I intend to further help this along by writing a D style guide, "The D Way". There's a start on it all ready with the document on how to do error handling: www.digitalmars.com/d/errors.html
Aug 24 2001
parent reply Dan Hursh <hursh infonet.isl.net> writes:
Walter wrote:
 
 Dan Hursh wrote in message <3B85F62A.3C3D8591 infonet.isl.net>...
 Single inheritance may be easier to implement, but you are losing
something.  It's a little concerning how often folks here take the
opinion that "Feature X has problems and I never use it anyway, so no
body else 'really' needs it."  I'm not specificly blaming you, but i've
lost track of how many time if seen that reasoning tonight.  I'm afraid
I'll see it a lot in the 275 I still have to read.

Your reasoning has merit. The counterargument (and I've discussed this at length with my colleagues) is that C++ gives you a dozen ways and styles to do X. Programmers tend to develop specific styles and do things in certain ways. This leads to one programmer's use of C++ to be radically different than another's, almost to the point where they are different languages. C++ is a huge language, and C++ programmers tend to learn particular "islands" in the language and not be too familiar with the rest of it. Hence one idea behind D is to *reduce* the number of ways X can be accomplished, and reduce the balkanization of programmer expertise. Then, one programmer's coding style will look more like another's, with the intended result that legacy D code will be more maintainable. For example, over the years I've seen dozens of different ways that debug code was inserted into a program, all very different. D has one way - with the debug attribute/statement. C++ has a dozen string classes plus the native C way of doing strings. D has one way of doing strings. I intend to further help this along by writing a D style guide, "The D Way". There's a start on it all ready with the document on how to do error handling: www.digitalmars.com/d/errors.html

I can accept the argument that multiple inheritance the C++ way gets ugly easily for reasons X, Y & Z and that we don't to copy it or throw in something just as bad. I just don't like the "I don't use it so no one should" type statements, like the one I was responding to, when I have used the feature often and well. In response to the above, I don't mean to demand multiple inheritance. I do believe it is handy in C++ when done right. It is unusable when done wrong. I just don't see single inheritance w/ interfaces as a full solution. Likewise with the lack arbitrary sized numeric types and operator overloads or matrix math and overloads, and a bunch of other debates going on. I just want to make sure we understand when we are saying X cannot be done. In the case of the message I responded to, we are saying there is no convenient way to reuse the code of two classes to create one that behaves as both. You either re-write the universe (or microcosm) with interface or you try to find a way to kludge with a has-a relationship. I guess I'm also a little scared that a debate will end with "I don't use it ...". I doubt you would end a debate that way and I owe you an apology for acting like you might. Along with what you said, C++ is too big. I hope D does not ever become like C++ in that sense. I like to see all the interest here, but in the end I would rather that D, in it entirety, be something that "feels right" inside one person's head (yours on this case) than to have it be the language that made it though a committee with the least amount of bickering. I had a longer response (like this isn't rambling), but I realized it could be simplified to: - perl v. python (/me is in the perl camp but understands the python camp) - one way to do things can lead to a steep learning curve if when too many features must be understood before you can apply the "One True Way" - a rant/example about Java's library and the problem it has caused at work this way. - too many "One True Way"s to understand - they get misused because the developer didn't pick/notice the better "One True Way". - C++ is the same because of it's library and the language itself - the increment nature of perl is good - you can learn a little bit at a time and still be productive - C had a steeper learning curve, but the shape of the curve resembled perl more than C++ I guess I'll hope D is a good incremental language. And I've also convinced myself that the library can be a very huge source of evil if it forces the user to understand all of it before he can use any of it. (perhaps the library should be a bunch of small, independent, compatible pieces?) I also hope we don't sacrifice the ability to solve reasonable classes of problems just because we don't like how C++ implemented the solution. I've been impressed with how some of the uses of the preprocessor have been dealt with and how some of the operator overloading uses have been dealt with. I trust that generic programming will make it into the language in a form that will make C++ programmer secretly jealous and that it is a matter of finding the better way. I admit that single inheritance w/ interfaces handles a lot more than just single inheritance, but the are still real code reuse issue that this doesn't address. The style guide would probably be a good discussion piece to start from for addressing things. It would be a good thing to look at and then ask "how do I do X". Either we update the guide, we update the language, or we say "you don't". It has to be a lot more productive than my ramblings. I guess I should shut up so you can finish the first pass on compiler and the guide then. Dan
Aug 25 2001
parent "Walter" <walter digitalmars.com> writes:
Dan Hursh wrote in message <3B875392.F1956643 infonet.isl.net>...
 I guess I'm also a little scared that a debate will end with "I don't
use it ...".  I doubt you would end a debate that way and I owe you an
apology for acting like you might.

Unfortunately, the reality is I can't implement a lot of great features. There are just too many.
 Along with what you said, C++ is too big.  I hope D does not ever
become like C++ in that sense.  I like to see all the interest here, but
in the end I would rather that D, in it entirety, be something that
"feels right" inside one person's head (yours on this case) than to have
it be the language that made it though a committee with the least amount
of bickering.

I think it feeling right is important.
 I guess I'll hope D is a good incremental language.  And I've also
convinced myself that the library can be a very huge source of evil if
it forces the user to understand all of it before he can use any of it.
(perhaps the library should be a bunch of small, independent, compatible
pieces?)

Yes, I want to make the library that way. The Java library turns out to be if you use any piece of it, you drag in the whole thing.
Aug 25 2001
prev sibling next sibling parent "Angus Graham" <agraham_d agraham.ca> writes:
"Dan Hursh" <hursh infonet.isl.net> wrote

...
 It's a little concerning how often folks here take the
 opinion that "Feature X has problems and I never use it anyway, so no
 body else 'really' needs it."  I'm not specificly blaming you, but i've
 lost track of how many time if seen that reasoning tonight.  I'm afraid
 I'll see it a lot in the 275 I still have to read.

I don't think this is such a bad argument. Everything it useful to someone right? So should everything be in then? No, you try balance the yeas with the nays in order to have the best language for the greatest number. I say nay to virtual inheritance because I don't use it and I wish the programs I maintain didn't either. If the majority says yea, well that's just one more thing I have to concern myself with whether I like it or not.
Aug 24 2001
prev sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Dan Hursh wrote:
 ...  It's a little concerning how often folks here take the
 opinion that "Feature X has problems and I never use it anyway,
 so no body else 'really' needs it."  I'm not specificly blaming
 you, but i've lost track of how many time if seen that reasoning
 tonight.  I'm afraid I'll see it a lot in the 275 I still have
  to read.
 
 Dan
 

one must remember K.I.S.S. OTOH, the basic features need to be present that will allow the language to grow over time into the "proper" shape. Were I arguing for what I want, I'd not only argue for multiple inheritance, but for an interpreter as well as a compiler, with linkages between the object code from the compiler and the code executing in the interpreter. That way features that couldn't be totally compiled would be able to have their flexible features managed by the interpreter, while the rest could run at compiled speed. And code that was simple enough could be unzipped from the interpreter, and run stand-alone. But let's be reasonable. Interfaces + delegation can handle almost all of what multiple inheritance can handle. If there are ways to expose the C compatible links, then Python or Ruby can be used for the interpretive layer. etc. If it gets too complex, it won't get done. People argue for what they want, and Walter *decides*. It MUST be that way.
Aug 24 2001
parent reply Dan Hursh <hursh infonet.isl.net> writes:
Charles Hixson wrote:
 
 Dan Hursh wrote:
 ...  It's a little concerning how often folks here take the
 opinion that "Feature X has problems and I never use it anyway,
 so no body else 'really' needs it."  I'm not specificly blaming
 you, but i've lost track of how many time if seen that reasoning
 tonight.  I'm afraid I'll see it a lot in the 275 I still have
  to read.

 Dan

one must remember K.I.S.S. OTOH, the basic features need to be present that will allow the language to grow over time into the "proper" shape. Were I arguing for what I want, I'd not only argue for multiple inheritance, but for an interpreter as well as a compiler, with linkages between the object code from the compiler and the code executing in the interpreter. That way features that couldn't be totally compiled would be able to have their flexible features managed by the interpreter, while the rest could run at compiled speed. And code that was simple enough could be unzipped from the interpreter, and run stand-alone. But let's be reasonable. Interfaces + delegation can handle almost all of what multiple inheritance can handle. If there are ways to expose the C compatible links, then Python or Ruby can be used for the interpretive layer. etc. If it gets too complex, it won't get done. People argue for what they want, and Walter *decides*. It MUST be that way.

True. I got whiny. It was past my bed time. I can agree with K.I.S.S. and growing the language. I am afraid of having a library based on the state of the language before it grew. Libraries are sometimes harder to change than languages once they are in use. Of course it will probably be a while until we have a library that sticks. OTOH, I don't buy the "I don't like it ..." argument. Walter is about the only one who can get away with that and from what I can tell that's not his style. I just don't want to give up on finding a good implementation of a good functionality just because someone else doesn't use or like a bad implementation of it. I'm still too young and idealistic for that.
Aug 25 2001
parent reply "Glen Dayton" <dayton timesten.com> writes:
When everything inherits from a common Object class, the argument for
templates does grow weaker.  C++ needs templates because there is no common
ancestor.

But when you write generic functions for arguments of class Object, you have
explicitly overridden type safety.

The original motivation behind templates was to provide a type-safe means of
replacing macros.  A template captures the concept of operations on classes
returning functions and other classes.  Templates, though, appear klugey
because they use a different syntax than normal functions and methods to
achieve their aims.

Likewise, when everything inherits from a common class, multiple inheritance
appears to be not so necessary.  Java tried to eliminate multiple
inheritance with interfaces -- but interfaces are nothing more than multiple
inheritance with abstract classes. (It looks like a horse, whinnies like a
horse, and smells like a horse...)

I'll mention my suggestion about multiple inheritance:

Implement, but make all inheritance virtual -- eliminate the virtual
keyword.
Aug 28 2001
next sibling parent "Kent Sandvik" <sandvik excitehome.net> writes:
"Glen Dayton" <dayton timesten.com> wrote in message
news:9mh7ah$1s7a$1 digitaldaemon.com...
 Likewise, when everything inherits from a common class, multiple

 appears to be not so necessary.  Java tried to eliminate multiple
 inheritance with interfaces -- but interfaces are nothing more than

 inheritance with abstract classes. (It looks like a horse, whinnies like a
 horse, and smells like a horse...)

The Java interface definition is more of a 'contact', something that the class has to implement. This is one of the best features Java ever implemented -- you could simulate this with an abstract class either directly inherited from, or then with multiple inheritance in C++, but opening up full multiple inheritance is not really needed. The few cases I've seen this tried out in production platforms, such as Pink's mixin classes, it was mostly to support 'contracts' or otherwise provide mixins for building far too complex code. I rather see a nice interface definition than MI, as MI will just cause all kinds of strange pains, namespace issues, which of the named member functions or fields should override, and other similar nastiness. --Kent
Aug 28 2001
prev sibling parent Dan Hursh <hursh infonet.isl.net> writes:
Glen Dayton wrote:
 
 When everything inherits from a common Object class, the argument for
 templates does grow weaker.  C++ needs templates because there is no common
 ancestor.

There are costs for this. To get template functionality from a base class, you have to use virtual functions (a performance hit) and you have to deal with type error at runtime when template work them out at compile time.
 But when you write generic functions for arguments of class Object, you have
 explicitly overridden type safety.

But with templates too, you still have to ability to choose type safety.
 The original motivation behind templates was to provide a type-safe means of
 replacing macros.  A template captures the concept of operations on classes
 returning functions and other classes.  

...at compile time, like macros.
 Templates, though, appear klugey
 because they use a different syntax than normal functions and methods to
 achieve their aims.

Not to me. Template appear kludgy because the syntax for them is ambiguous and has to be backward compatible with C. I don't know if that got compatibility, but it is definitely backward.
 Likewise, when everything inherits from a common class, multiple inheritance
 appears to be not so necessary.  Java tried to eliminate multiple
 inheritance with interfaces -- but interfaces are nothing more than multiple
 inheritance with abstract classes. (It looks like a horse, whinnies like a
 horse, and smells like a horse...)

There are cases of code reuse that single inheritance (even with interfaces) do not handle well. You can say they handle those cases the same way you can say C handles type based dynamic dispatch. You can kludge it in, but you feel dirty in the morning.
 I'll mention my suggestion about multiple inheritance:
 
 Implement, but make all inheritance virtual -- eliminate the virtual
 keyword.

Dan
Aug 28 2001