www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - To interface or not to interface

reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
A long discussion on the utility of interfaces has taken place on the  
announce newsgroup following my announcement of dcollections for D2.

Dcollections supports interfaces peripherally.  Meaning the types in  
dcollections are essentially concrete classes with interfaces tacked on  
for cases where interfaces make sense.

Whenever I've used dcollections (D1 included) I don't think I've ever used  
the interface for a container, only the concrete type.  But within  
dcollections, there's some functions that use the interface type to do  
(IMO) nifty things like compare two sets that have different  
implementations.

The general consensus is that interfaces for dcollections do not add any  
meaningful value, and that I should do everything with generic programming  
and templates.  My view is that interfaces are useful for binary  
compatibility and in reducing the footprint of executables.  But that  
doesn't mean much at the moment, because D is statically linked.

I'd ask the naysayers of interfaces for dcollections, and also the  
supporters: what is the point of having interfaces in D?  Are interfaces  
pretty much obsolete, and I am just nostalgic about their utility?

What do you think?

-Steve
May 24 2010
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 I'd ask the naysayers of interfaces for dcollections, and also the 
 supporters: what is the point of having interfaces in D?  Are interfaces 
 pretty much obsolete, and I am just nostalgic about their utility?
Interfaces are for runtime polymorphism, rather than compile time polymorphism. They are especially useful for things like: 1. runtime plugin interfaces 2. designs where strict implementation hiding is desired 3. to have binary libraries (shared and static) 5. reduced code memory footprint 6. experience shows they are an excellent fit for user interfaces Compile time polymorphism, such as what templates provide, are most useful for: 1. maximum performance 2. minimal data memory consumption 3. better compile time checking I believe the tradeoffs for collection types favor compile time polymorphism because: 1. performance is often critical for collections 2. C++ STL has shown the success of this approach 3. collections must fit in naturally with ranges, and ranges are compile time polymorphic
May 24 2010
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 May 2010 14:10:26 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 I'd ask the naysayers of interfaces for dcollections, and also the  
 supporters: what is the point of having interfaces in D?  Are  
 interfaces pretty much obsolete, and I am just nostalgic about their  
 utility?
Interfaces are for runtime polymorphism, rather than compile time polymorphism. They are especially useful for things like: 1. runtime plugin interfaces 2. designs where strict implementation hiding is desired 3. to have binary libraries (shared and static) 5. reduced code memory footprint 6. experience shows they are an excellent fit for user interfaces Compile time polymorphism, such as what templates provide, are most useful for: 1. maximum performance 2. minimal data memory consumption 3. better compile time checking I believe the tradeoffs for collection types favor compile time polymorphism because: 1. performance is often critical for collections 2. C++ STL has shown the success of this approach 3. collections must fit in naturally with ranges, and ranges are compile time polymorphic
I'd counter point 2 by saying that 1. C++ classes are value-types by default and 2. C++ doesn't have interfaces, so it's not exactly fair to say that the STL author considered interfaces but rejected them. and on point 3, why is it not OK to *also* provide interfaces in addition to ranges as dcollections does? That is, take away dcollections' interfaces, and you have essentially compile-time polymorphism, they all support ranges etc. Interfaces are also there in case you want to use them in things like runtime plugin interfaces. Basically, my point is, compile time interfaces does not mean you can't also have runtime interfaces. In fact, interfaces can be compile-time parameterized. Also, much of a user interface consists of various collections (listview, treeview, child widgets, etc.). Why is runtime polymorphism good there, but not on a generic collections package (not as the only means of access of course)? -Steve
May 24 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 On Mon, 24 May 2010 14:10:26 -0400, Walter Bright 
 <newshound1 digitalmars.com> wrote:
 
 Steven Schveighoffer wrote:
 I'd ask the naysayers of interfaces for dcollections, and also the 
 supporters: what is the point of having interfaces in D?  Are 
 interfaces pretty much obsolete, and I am just nostalgic about their 
 utility?
Interfaces are for runtime polymorphism, rather than compile time polymorphism. They are especially useful for things like: 1. runtime plugin interfaces 2. designs where strict implementation hiding is desired 3. to have binary libraries (shared and static) 5. reduced code memory footprint 6. experience shows they are an excellent fit for user interfaces Compile time polymorphism, such as what templates provide, are most useful for: 1. maximum performance 2. minimal data memory consumption 3. better compile time checking I believe the tradeoffs for collection types favor compile time polymorphism because: 1. performance is often critical for collections 2. C++ STL has shown the success of this approach 3. collections must fit in naturally with ranges, and ranges are compile time polymorphic
I'd counter point 2 by saying that 1. C++ classes are value-types by default and 2. C++ doesn't have interfaces, so it's not exactly fair to say that the STL author considered interfaces but rejected them.
C++ certainly does have interfaces. The whole COM system is based on them, for example. Technically, D interfaces are just a subset of C++ multiple inheritance.
 and on point 3, why is it not OK to *also* provide interfaces in 
 addition to ranges as dcollections does?  That is, take away 
 dcollections' interfaces, and you have essentially compile-time 
 polymorphism, they all support ranges etc.  Interfaces are also there in 
 case you want to use them in things like runtime plugin interfaces.
The best reason I can think of is to avoid kitchen-sink style components. Components should do one thing well. Adding capability should be done with aggregation by the user.
 Basically, my point is, compile time interfaces does not mean you can't 
 also have runtime interfaces.  In fact, interfaces can be compile-time 
 parameterized.
Sure, but I'd argue that adding such runtime polymorphism should be done with a separate add-on component. It should not be part of the collection component.
 Also, much of a user interface consists of various collections 
 (listview, treeview, child widgets, etc.).  Why is runtime polymorphism 
 good there, but not on a generic collections package (not as the only 
 means of access of course)?
A user interface object is not a collection component, I think there's a confusion in the design there.
May 24 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 May 2010 14:36:57 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 On Mon, 24 May 2010 14:10:26 -0400, Walter Bright  
 <newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 I'd ask the naysayers of interfaces for dcollections, and also the  
 supporters: what is the point of having interfaces in D?  Are  
 interfaces pretty much obsolete, and I am just nostalgic about their  
 utility?
Interfaces are for runtime polymorphism, rather than compile time polymorphism. They are especially useful for things like: 1. runtime plugin interfaces 2. designs where strict implementation hiding is desired 3. to have binary libraries (shared and static) 5. reduced code memory footprint 6. experience shows they are an excellent fit for user interfaces Compile time polymorphism, such as what templates provide, are most useful for: 1. maximum performance 2. minimal data memory consumption 3. better compile time checking I believe the tradeoffs for collection types favor compile time polymorphism because: 1. performance is often critical for collections 2. C++ STL has shown the success of this approach 3. collections must fit in naturally with ranges, and ranges are compile time polymorphic
I'd counter point 2 by saying that 1. C++ classes are value-types by default and 2. C++ doesn't have interfaces, so it's not exactly fair to say that the STL author considered interfaces but rejected them.
C++ certainly does have interfaces. The whole COM system is based on them, for example. Technically, D interfaces are just a subset of C++ multiple inheritance.
And if STL looked like COM, I think it would have been a miserable failure indeed.
 and on point 3, why is it not OK to *also* provide interfaces in  
 addition to ranges as dcollections does?  That is, take away  
 dcollections' interfaces, and you have essentially compile-time  
 polymorphism, they all support ranges etc.  Interfaces are also there  
 in case you want to use them in things like runtime plugin interfaces.
The best reason I can think of is to avoid kitchen-sink style components. Components should do one thing well. Adding capability should be done with aggregation by the user.
What if it can do both things well (I would propose that dcollections does)?
 Basically, my point is, compile time interfaces does not mean you can't  
 also have runtime interfaces.  In fact, interfaces can be compile-time  
 parameterized.
Sure, but I'd argue that adding such runtime polymorphism should be done with a separate add-on component. It should not be part of the collection component.
So I should specifically have to wrap a collection type in order to make it runtime polymorphic, forwarding all the operations to the collection? Essentially something like: class WrappedSet(Impl, V) : Set!V { Impl!V impl; bool contains(V v) { return impl.contains(v);} ... } For what reason? Why is it so bad to just stick Set!V on the end of the implementation class?
 Also, much of a user interface consists of various collections  
 (listview, treeview, child widgets, etc.).  Why is runtime polymorphism  
 good there, but not on a generic collections package (not as the only  
 means of access of course)?
A user interface object is not a collection component, I think there's a confusion in the design there.
Don't user interface objects have data? If a UI component is an interface, how does it expose access to its data? For example, a .NET ListView control contains an Items property which you can use to access the elements in the list view. The Items property returns a ListViewItemCollection which implements IList, IContainer, and IEnumerable. I've found these types of abstractions useful when adding/iterating, etc. -Steve
May 24 2010
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 On Mon, 24 May 2010 14:36:57 -0400, Walter Bright 
 <newshound1 digitalmars.com> wrote:
 and on point 3, why is it not OK to *also* provide interfaces in 
 addition to ranges as dcollections does?  That is, take away 
 dcollections' interfaces, and you have essentially compile-time 
 polymorphism, they all support ranges etc.  Interfaces are also there 
 in case you want to use them in things like runtime plugin interfaces.
The best reason I can think of is to avoid kitchen-sink style components. Components should do one thing well. Adding capability should be done with aggregation by the user.
What if it can do both things well (I would propose that dcollections does)?
Probably for the same reason I don't want a microwave built in to my TV set. It's not a question of can it do both well, it's a question of is it a distinct component or not.
 Sure, but I'd argue that adding such runtime polymorphism should be 
 done with a separate add-on component. It should not be part of the 
 collection component.
So I should specifically have to wrap a collection type in order to make it runtime polymorphic, forwarding all the operations to the collection? Essentially something like: class WrappedSet(Impl, V) : Set!V { Impl!V impl; bool contains(V v) { return impl.contains(v);} ... } For what reason? Why is it so bad to just stick Set!V on the end of the implementation class?
Because then everyone who just wants a hash table winds up carrying around the complexity for things they don't want. The idea behind pluggable components is that each component should be minimal, and then the user aggregates them to meet his needs.
 Also, much of a user interface consists of various collections 
 (listview, treeview, child widgets, etc.).  Why is runtime 
 polymorphism good there, but not on a generic collections package 
 (not as the only means of access of course)?
A user interface object is not a collection component, I think there's a confusion in the design there.
Don't user interface objects have data? If a UI component is an interface, how does it expose access to its data?
That's up to the UI interface designer. It has nothing to do with how it implements the collection under the hood.
 For example, a .NET 
 ListView control contains an Items property which you can use to access 
 the elements in the list view.  The Items property returns a 
 ListViewItemCollection which implements IList, IContainer, and 
 IEnumerable.  I've found these types of abstractions useful when 
 adding/iterating, etc.
A graphical component can wrap a collection component. I see no reason why the collection needs to have runtime polymorphism to enable that.
May 24 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 May 2010 16:27:40 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 On Mon, 24 May 2010 14:36:57 -0400, Walter Bright  
 <newshound1 digitalmars.com> wrote:
 and on point 3, why is it not OK to *also* provide interfaces in  
 addition to ranges as dcollections does?  That is, take away  
 dcollections' interfaces, and you have essentially compile-time  
 polymorphism, they all support ranges etc.  Interfaces are also there  
 in case you want to use them in things like runtime plugin interfaces.
The best reason I can think of is to avoid kitchen-sink style components. Components should do one thing well. Adding capability should be done with aggregation by the user.
What if it can do both things well (I would propose that dcollections does)?
Probably for the same reason I don't want a microwave built in to my TV set. It's not a question of can it do both well, it's a question of is it a distinct component or not.
We're not talking microwave and TV set here. We're talking more like microwave and a cooking device.
 Sure, but I'd argue that adding such runtime polymorphism should be  
 done with a separate add-on component. It should not be part of the  
 collection component.
So I should specifically have to wrap a collection type in order to make it runtime polymorphic, forwarding all the operations to the collection? Essentially something like: class WrappedSet(Impl, V) : Set!V { Impl!V impl; bool contains(V v) { return impl.contains(v);} ... } For what reason? Why is it so bad to just stick Set!V on the end of the implementation class?
Because then everyone who just wants a hash table winds up carrying around the complexity for things they don't want. The idea behind pluggable components is that each component should be minimal, and then the user aggregates them to meet his needs.
What extra complexity? That's what I'm trying to get at, there is none. The complexity of "being a set" is builtin to the fact that HashSet is a set!
 Also, much of a user interface consists of various collections  
 (listview, treeview, child widgets, etc.).  Why is runtime  
 polymorphism good there, but not on a generic collections package  
 (not as the only means of access of course)?
A user interface object is not a collection component, I think there's a confusion in the design there.
Don't user interface objects have data? If a UI component is an interface, how does it expose access to its data?
That's up to the UI interface designer. It has nothing to do with how it implements the collection under the hood.
 For example, a .NET ListView control contains an Items property which  
 you can use to access the elements in the list view.  The Items  
 property returns a ListViewItemCollection which implements IList,  
 IContainer, and IEnumerable.  I've found these types of abstractions  
 useful when adding/iterating, etc.
A graphical component can wrap a collection component. I see no reason why the collection needs to have runtime polymorphism to enable that.
It's a logical conclusion. You provide a map-type collection, call it a HashMap. Then, a UI designer wants to abstract his specific Map-like container that exposes his elements, so you provide him a Map interface. But HashMap implements all the required functions to be able to implement the Map interface, so you slap Map on the back of the class definition, and presto! It implements the map interface. Where's the extra complexity? I don't see how that's a bad thing. -Steve
May 24 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 It's a logical conclusion.  You provide a map-type collection, call it a 
 HashMap.  Then, a UI designer wants to abstract his specific Map-like 
 container that exposes his elements, so you provide him a Map 
 interface.  But HashMap implements all the required functions to be able 
 to implement the Map interface, so you slap Map on the back of the class 
 definition, and presto!  It implements the map interface.  Where's the 
 extra complexity?
The extra complexity is in the container supporting very different ways to do the same thing.
 I don't see how that's a bad thing.
If the user wants an interface, he can add one on to the front of the collection. It doesn't need to be in the collection itself. The "bad" thing is the component integrating in capability that is easily done as an add-on. This reminds me of a fellow I worked with years ago who would provide both a free-function interface and a class interface to each of his components. It was completely redundant to do both, and added pages of complexity and documentation.
May 24 2010
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 May 2010 17:11:49 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 It's a logical conclusion.  You provide a map-type collection, call it  
 a HashMap.  Then, a UI designer wants to abstract his specific Map-like  
 container that exposes his elements, so you provide him a Map  
 interface.  But HashMap implements all the required functions to be  
 able to implement the Map interface, so you slap Map on the back of the  
 class definition, and presto!  It implements the map interface.   
 Where's the extra complexity?
The extra complexity is in the container supporting very different ways to do the same thing.
I don't see this being "very different": coll.add(coll2); // uses coll2 as interface. coll.add(coll2[0..5]); // uses coll2 slice as a range. coll.add(arr); // adds an array.
 I don't see how that's a bad thing.
If the user wants an interface, he can add one on to the front of the collection. It doesn't need to be in the collection itself.
We're going in circles here. I feel like pointing back to my previous post with the wrapping example... All an interface does is give an abstract representation of functions that are *already there*. Removing the interface does not remove the functions that implemented the interface.
 The "bad" thing is the component integrating in capability that is  
 easily done as an add-on.

 This reminds me of a fellow I worked with years ago who would provide  
 both a free-function interface and a class interface to each of his  
 components. It was completely redundant to do both, and added pages of  
 complexity and documentation.
This is completely different, I'm tacking on one small piece of declaration and the class becomes an interface. I'm not reimplementing or wrapping anything. Surely you can see that. In a related story, I *did* have to do something like this when someone wanted a Java interface to some C++ code we had. In order to do this, we had to declare extern "C" functions that Java could interface via JNI. -Steve
May 24 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 All an interface does is give an abstract representation of functions 
 that are *already there*.  Removing the interface does not remove the 
 functions that implemented the interface.
Then why do interfaces need to be part of the collection component? Why can't the user add them if he wants them?
May 24 2010
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 May 2010 18:13:38 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 All an interface does is give an abstract representation of functions  
 that are *already there*.  Removing the interface does not remove the  
 functions that implemented the interface.
Then why do interfaces need to be part of the collection component? Why can't the user add them if he wants them?
How do you add an interface to a class? Wrapping seems like it would add more overhead than just implementing the interface, especially since D's inliner has some strange restrictions. -Steve
May 25 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 On Mon, 24 May 2010 18:13:38 -0400, Walter Bright 
 <newshound1 digitalmars.com> wrote:
 
 Steven Schveighoffer wrote:
 All an interface does is give an abstract representation of functions 
 that are *already there*.  Removing the interface does not remove the 
 functions that implemented the interface.
Then why do interfaces need to be part of the collection component? Why can't the user add them if he wants them?
How do you add an interface to a class?
Define an interface who's member functions call the class' member functions.
May 25 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 25 May 2010 23:29:34 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 On Mon, 24 May 2010 18:13:38 -0400, Walter Bright  
 <newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 All an interface does is give an abstract representation of functions  
 that are *already there*.  Removing the interface does not remove the  
 functions that implemented the interface.
Then why do interfaces need to be part of the collection component? Why can't the user add them if he wants them?
How do you add an interface to a class?
Define an interface who's member functions call the class' member functions.
Quoting from earlier message: Wrapping seems like it would add more overhead than just implementing the interface, especially since D's inliner has some strange restrictions. -Steve
May 27 2010
parent Martin Hinsch <hinsch.martin gmail.com> writes:
== Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 On Tue, 25 May 2010 23:29:34 -0400, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Steven Schveighoffer wrote:
 On Mon, 24 May 2010 18:13:38 -0400, Walter Bright
 <newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 All an interface does is give an abstract representation of functions
 that are *already there*.  Removing the interface does not remove the
 functions that implemented the interface.
Then why do interfaces need to be part of the collection component? Why can't the user add them if he wants them?
How do you add an interface to a class?
Define an interface who's member functions call the class' member functions.
Quoting from earlier message: Wrapping seems like it would add more overhead than just implementing the interface, especially since D's inliner has some strange restrictions. -Steve
I think the main problem is that adding an interface post-hoc on the user's side is a cumbersome error-prone process. This problem has been avoided quite elegantly by Go by the way, which allows interfaces to be "applied" to all types that conform without requiring inheritance (can't resist to mention that I proposed this exact approach some years ago (long before Go was released) in this news group ;-) ). cheers Martin
May 27 2010
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2010-05-25 00.13, Walter Bright wrote:
 Steven Schveighoffer wrote:
 All an interface does is give an abstract representation of functions
 that are *already there*. Removing the interface does not remove the
 functions that implemented the interface.
Then why do interfaces need to be part of the collection component? Why can't the user add them if he wants them?
How would the user do that? The user would need to create an interface and then a wrapper that implements the interface. An interface without implementations is useless. Or am I missing something ? -- /Jacob Carlborg
May 25 2010
prev sibling parent Alex Makhotin <alex bitprox.com> writes:
Walter Bright wrote:
  
 The extra complexity is in the container supporting very different ways 
 to do the same thing.
 
A I understand from the current discussion, interfaces meet a strong debate. And yes, I also understand that the interfaces are considered unnecessary bloat that makes execution speed slower which is critical for performance. Personally, I think they should be accessible. But, can there be a compromise to satisfy both sides? To illustrate the idea:
 The partial modifier indicates that additional parts of the type declaration
may exist elsewhere, but the existence of such additional parts is not a
requirement;
I see as a potential benefit for the D to have some sort of it. For those who need templates for speed, or any other motive: 1. Import .di header with needed partial declaration of the required type. 2. The compiler injects exactly the code generated from the implementation of the header and nothing more. For those who need interfaces for the reasons of maintainability, compatibility and interoperability: 1. Import .di header with needed partial declaration of the required type interfaces. 2. The compiler injects exactly the code generated from the implementation of the header or links with the provided library with the exact implementation. The user source includes required .di header, which declares partial type declarations. The specified partial types from the header may have the implementation, but this should not be a requirement. In case of partially not implemented type the user gets 'not implemented' error, linkage fault, or something similar. Is there a possibility for such solution? -- Alex Makhotin, the founder of BITPROX, http://bitprox.com
May 24 2010
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2010-05-24 21.08, Steven Schveighoffer wrote:
 On Mon, 24 May 2010 14:36:57 -0400, Walter Bright
 <newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 On Mon, 24 May 2010 14:10:26 -0400, Walter Bright
 <newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:
 I'd ask the naysayers of interfaces for dcollections, and also the
 supporters: what is the point of having interfaces in D? Are
 interfaces pretty much obsolete, and I am just nostalgic about
 their utility?
Interfaces are for runtime polymorphism, rather than compile time polymorphism. They are especially useful for things like: 1. runtime plugin interfaces 2. designs where strict implementation hiding is desired 3. to have binary libraries (shared and static) 5. reduced code memory footprint 6. experience shows they are an excellent fit for user interfaces Compile time polymorphism, such as what templates provide, are most useful for: 1. maximum performance 2. minimal data memory consumption 3. better compile time checking I believe the tradeoffs for collection types favor compile time polymorphism because: 1. performance is often critical for collections 2. C++ STL has shown the success of this approach 3. collections must fit in naturally with ranges, and ranges are compile time polymorphic
I'd counter point 2 by saying that 1. C++ classes are value-types by default and 2. C++ doesn't have interfaces, so it's not exactly fair to say that the STL author considered interfaces but rejected them.
C++ certainly does have interfaces. The whole COM system is based on them, for example. Technically, D interfaces are just a subset of C++ multiple inheritance.
And if STL looked like COM, I think it would have been a miserable failure indeed.
 and on point 3, why is it not OK to *also* provide interfaces in
 addition to ranges as dcollections does? That is, take away
 dcollections' interfaces, and you have essentially compile-time
 polymorphism, they all support ranges etc. Interfaces are also there
 in case you want to use them in things like runtime plugin interfaces.
The best reason I can think of is to avoid kitchen-sink style components. Components should do one thing well. Adding capability should be done with aggregation by the user.
What if it can do both things well (I would propose that dcollections does)?
 Basically, my point is, compile time interfaces does not mean you
 can't also have runtime interfaces. In fact, interfaces can be
 compile-time parameterized.
Sure, but I'd argue that adding such runtime polymorphism should be done with a separate add-on component. It should not be part of the collection component.
So I should specifically have to wrap a collection type in order to make it runtime polymorphic, forwarding all the operations to the collection? Essentially something like: class WrappedSet(Impl, V) : Set!V { Impl!V impl; bool contains(V v) { return impl.contains(v);} ... } For what reason? Why is it so bad to just stick Set!V on the end of the implementation class?
 Also, much of a user interface consists of various collections
 (listview, treeview, child widgets, etc.). Why is runtime
 polymorphism good there, but not on a generic collections package
 (not as the only means of access of course)?
A user interface object is not a collection component, I think there's a confusion in the design there.
Don't user interface objects have data? If a UI component is an interface, how does it expose access to its data? For example, a .NET ListView control contains an Items property which you can use to access the elements in the list view. The Items property returns a ListViewItemCollection which implements IList, IContainer, and IEnumerable. I've found these types of abstractions useful when adding/iterating, etc. -Steve
I would say that is a bad design, I would go with the MVC pattern. For example, you have a ListView and when it's ready to display, say row 3, it calls your delegate and request you to return the item that should be visible on row 3. Then it's up to you to store the items in some appropriate data structure, like a list or array. -- /Jacob Carlborg
May 25 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 25 May 2010 09:26:20 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2010-05-24 21.08, Steven Schveighoffer wrote:
 Don't user interface objects have data? If a UI component is an
 interface, how does it expose access to its data? For example, a .NET
 ListView control contains an Items property which you can use to access
 the elements in the list view. The Items property returns a
 ListViewItemCollection which implements IList, IContainer, and
 IEnumerable. I've found these types of abstractions useful when
 adding/iterating, etc.
 -Steve
I would say that is a bad design, I would go with the MVC pattern. For example, you have a ListView and when it's ready to display, say row 3, it calls your delegate and request you to return the item that should be visible on row 3. Then it's up to you to store the items in some appropriate data structure, like a list or array.
I don't know if a delegate is enough to implement the pattern. What you need is a set of delegates that perform operations on the container. Oh wait, that's an interface! One interesting difference between an interface and a delegate is that an interface is a single pointer, whereas a delegate is two. With the context-pointer design, many more features are possible. For instance, struct interfaces would be easy, as well as easily tacking on an interface to a class. In any case, Windows Forms is probably the easiest UI toolkit I've worked with (which isn't saying much), I don't think it's a bad design. That could be the Visual Studio talking though :) -Steve
May 25 2010
parent reply Jacob Carlborg <doob me.com> writes:
On 2010-05-25 15.38, Steven Schveighoffer wrote:
 On Tue, 25 May 2010 09:26:20 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2010-05-24 21.08, Steven Schveighoffer wrote:
 Don't user interface objects have data? If a UI component is an
 interface, how does it expose access to its data? For example, a .NET
 ListView control contains an Items property which you can use to access
 the elements in the list view. The Items property returns a
 ListViewItemCollection which implements IList, IContainer, and
 IEnumerable. I've found these types of abstractions useful when
 adding/iterating, etc.
 -Steve
I would say that is a bad design, I would go with the MVC pattern. For example, you have a ListView and when it's ready to display, say row 3, it calls your delegate and request you to return the item that should be visible on row 3. Then it's up to you to store the items in some appropriate data structure, like a list or array.
I don't know if a delegate is enough to implement the pattern. What you need is a set of delegates that perform operations on the container. Oh wait, that's an interface!
What I was trying to say is that a ListView should not contain a data structure. I try to explain that I want to say in code instead: auto data = new Item[10]; auto listView = new ListView; listView.numberOfRows = size_t delegate (ListView lv) { return data.length; } listView.itemAtRow = Item delegate (ListView lv, size_t row) { return data[row]; } Now Item could be an interface but it don't have to be. I suggest you have a look at Apple's documentation of NSTableView: * http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTableView_Class/Refe ence/Reference.html *
 One interesting difference between an interface and a delegate is that
 an interface is a single pointer, whereas a delegate is two. With the
 context-pointer design, many more features are possible. For instance,
 struct interfaces would be easy, as well as easily tacking on an
 interface to a class.

 In any case, Windows Forms is probably the easiest UI toolkit I've
 worked with (which isn't saying much), I don't think it's a bad design.
 That could be the Visual Studio talking though :)
I suggest you have a look at Cocoa, it uses the MVC pattern.
 -Steve
-- /Jacob Carlborg
May 25 2010
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 25 May 2010 10:01:48 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2010-05-25 15.38, Steven Schveighoffer wrote:
 On Tue, 25 May 2010 09:26:20 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2010-05-24 21.08, Steven Schveighoffer wrote:
 Don't user interface objects have data? If a UI component is an
 interface, how does it expose access to its data? For example, a .NET
 ListView control contains an Items property which you can use to  
 access
 the elements in the list view. The Items property returns a
 ListViewItemCollection which implements IList, IContainer, and
 IEnumerable. I've found these types of abstractions useful when
 adding/iterating, etc.
 -Steve
I would say that is a bad design, I would go with the MVC pattern. For example, you have a ListView and when it's ready to display, say row 3, it calls your delegate and request you to return the item that should be visible on row 3. Then it's up to you to store the items in some appropriate data structure, like a list or array.
I don't know if a delegate is enough to implement the pattern. What you need is a set of delegates that perform operations on the container. Oh wait, that's an interface!
What I was trying to say is that a ListView should not contain a data structure. I try to explain that I want to say in code instead: auto data = new Item[10]; auto listView = new ListView; listView.numberOfRows = size_t delegate (ListView lv) { return data.length; } listView.itemAtRow = Item delegate (ListView lv, size_t row) { return data[row]; }
Yes, I get that. What I'm saying is this is basically an interface. The difference is that the interface is not required to be declared on the container class, and requires 2 words of storage in the ListView per function instead of 1 word for all the functions. Another way to do this is: listView.items = data; Where listView.items is an interface that contains the functions you need. If the set of functions is complex, then using the delegates could be tedious. It's just a different way of doing it. There are benefits to both ways. Using the delegates is more flexible because a delegate does not need to be defined in a class with a predefined interface being implemented. It's also much easier to build a bunch of delegates on the fly rather than build an interface implementation. -Steve
May 25 2010
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-05-25 10:01:48 -0400, Jacob Carlborg <doob me.com> said:

 Now Item could be an interface but it don't have to be. I suggest you 
 have a look at Apple's documentation of NSTableView:
What Cocoa is doing is basically allowing 'optional' methods in an interface (a protocol in Objective-C). Taking your example, the NSTableViewDataSource protocol contains a lot of functions to provide the required data to a table. But many of them are optional: for instance a data source that does not implement the "...setObjectValue..." method will prevent the table's content from being edited, one that doesn't implement the "...sortDescriptorsDidChange..." method prevents the table from being sorted by clicking on its column headers, one that doesn't implement the various methods for drag and drop will prevent rows from being dragged. (Note to Cocoa programmers: Prior to the Mac OS X 10.6 SDK, NSTableViewDataSource was an informal protocol implemented as a category of unimplemented functions in NSObject. The 10.6 SDK changed it to be a formal protocol with optional methods, a feature added to Objective-C 2.0.) In D, one could create one interface for each of these groups of things, but then you'll have a bazilion of small interfaces and either you lose the relation between them or you end up with a combinational explosion. For instance, let's create a bunch of interfaces for what I wrote above: interface TableDataSource {...} interface TableDataSourceEdit : TableDataSource {...} interface TableDataSourceSort : TableDataSource {...} interface TableDataSourceDrag : TableDataSource {...} interface TableDataSourceDropTarget : TableDataSource {...} Now, when I implement the table view I could have one data source class TableView { TableDataSource dataSource; } and then I'd dynamically check whether my data source implements each of the child interfaces: auto dataSourceEdit = cast(TableDataSourceEdit)dataSource) if (dataSourceEdit) { dataSourceEdit.setObject(object, row, column); } else { // data source cannot be edited } That's essentially what is done in Cocoa, except that in Cocoa an object usually checks for the existence of one of its delegate function prior calling it instead of having a whole lot of interfaces. This is why protocols are allowed to have optional methods. Perhaps interfaces could be allowed to have optional methods that would require you to check if they're implemented before use. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 25 2010
parent reply Jacob Carlborg <doob me.com> writes:
On 2010-05-25 17.03, Michel Fortin wrote:
 On 2010-05-25 10:01:48 -0400, Jacob Carlborg <doob me.com> said:

 Now Item could be an interface but it don't have to be. I suggest you
 have a look at Apple's documentation of NSTableView:
What Cocoa is doing is basically allowing 'optional' methods in an interface (a protocol in Objective-C). Taking your example, the NSTableViewDataSource protocol contains a lot of functions to provide the required data to a table. But many of them are optional: for instance a data source that does not implement the "...setObjectValue..." method will prevent the table's content from being edited, one that doesn't implement the "...sortDescriptorsDidChange..." method prevents the table from being sorted by clicking on its column headers, one that doesn't implement the various methods for drag and drop will prevent rows from being dragged.
I've always thought that this design and the similar Java uses with interfaces, anonymous classes and adapters is just a design chosen because the languages are limited, don't support delegates.
 (Note to Cocoa programmers: Prior to the Mac OS X 10.6 SDK,
 NSTableViewDataSource was an informal protocol implemented as a category
 of unimplemented functions in NSObject. The 10.6 SDK changed it to be a
 formal protocol with optional methods, a feature added to Objective-C 2.0.)

 In D, one could create one interface for each of these groups of things,
 but then you'll have a bazilion of small interfaces and either you lose
 the relation between them or you end up with a combinational explosion.
 For instance, let's create a bunch of interfaces for what I wrote above:

 interface TableDataSource {...}
 interface TableDataSourceEdit : TableDataSource {...}
 interface TableDataSourceSort : TableDataSource {...}
 interface TableDataSourceDrag : TableDataSource {...}
 interface TableDataSourceDropTarget : TableDataSource {...}

 Now, when I implement the table view I could have one data source

 class TableView {
 TableDataSource dataSource;
 }

 and then I'd dynamically check whether my data source implements each of
 the child interfaces:

 auto dataSourceEdit = cast(TableDataSourceEdit)dataSource)
 if (dataSourceEdit) {
 dataSourceEdit.setObject(object, row, column);
 } else {
 // data source cannot be edited
 }

 That's essentially what is done in Cocoa, except that in Cocoa an object
 usually checks for the existence of one of its delegate function prior
 calling it instead of having a whole lot of interfaces. This is why
 protocols are allowed to have optional methods.

 Perhaps interfaces could be allowed to have optional methods that would
 require you to check if they're implemented before use.
How would you check if a method is implemented or not ? -- /Jacob Carlborg
May 26 2010
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-05-26 06:55:14 -0400, Jacob Carlborg <doob me.com> said:

 On 2010-05-25 17.03, Michel Fortin wrote:
 On 2010-05-25 10:01:48 -0400, Jacob Carlborg <doob me.com> said:
 
 Now Item could be an interface but it don't have to be. I suggest you
 have a look at Apple's documentation of NSTableView:
What Cocoa is doing is basically allowing 'optional' methods in an interface (a protocol in Objective-C). Taking your example, the NSTableViewDataSource protocol contains a lot of functions to provide the required data to a table. But many of them are optional: for instance a data source that does not implement the "...setObjectValue..." method will prevent the table's content from being edited, one that doesn't implement the "...sortDescriptorsDidChange..." method prevents the table from being sorted by clicking on its column headers, one that doesn't implement the various methods for drag and drop will prevent rows from being dragged.
I've always thought that this design and the similar Java uses with interfaces, anonymous classes and adapters is just a design chosen because the languages are limited, don't support delegates.
Indeed. Well, I think you could use runtime-reflection to achieve the same thing in Java, but that would make your interfaces "informal", in the sense that you just can't put those optional functions in the interfaces. (And it'd uglify the code a lot.) Technically, this is what was done prior Mac OS X 10.6 with informal protocols; I say "almost" since the method signatures being added as unimplemented categories to NSObject, they could still checked for correctness by the compiler.
 Perhaps interfaces could be allowed to have optional methods that would
 require you to check if they're implemented before use.
How would you check if a method is implemented or not ?
In Objective-C, it's quite easy. For instance, I've made my own subclass of NSTableView querying the delegate for a background color for a given row. So here's how you use the delegate: id delegate = [self delegate]; if ([delegate respondsToSelector: selector(tableView:backgroundColorForRow:)]) { NSColor *color = [delegate tableView:self backgroundColorForRow:row]; ...draw background color... } In D you can't do that currently, but here's how it could be added. First add an optional attribute for interface members: interface TableDelegate { ... optional NSColor backgroundColorForRow(NSTableView table, size_t row); } Classes implementing the interface can omit the optional methods. When omitted, the interface function is null. When using the interface, you must check first if the optional function is implemented by making sure the address isn't null: TableDelegate delegate = this.delegate; if (&delegate.backgroundColorForRow != null) { NSColor color = delegate.backgroundColorForRow(this, row); ...draw background color... } This would work somewhat similarily to weak-linked symbols. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 26 2010
parent reply Jacob Carlborg <doob me.com> writes:
On 2010-05-26 16.20, Michel Fortin wrote:
 On 2010-05-26 06:55:14 -0400, Jacob Carlborg <doob me.com> said:

 On 2010-05-25 17.03, Michel Fortin wrote:
 On 2010-05-25 10:01:48 -0400, Jacob Carlborg <doob me.com> said:

 Now Item could be an interface but it don't have to be. I suggest you
 have a look at Apple's documentation of NSTableView:
What Cocoa is doing is basically allowing 'optional' methods in an interface (a protocol in Objective-C). Taking your example, the NSTableViewDataSource protocol contains a lot of functions to provide the required data to a table. But many of them are optional: for instance a data source that does not implement the "...setObjectValue..." method will prevent the table's content from being edited, one that doesn't implement the "...sortDescriptorsDidChange..." method prevents the table from being sorted by clicking on its column headers, one that doesn't implement the various methods for drag and drop will prevent rows from being dragged.
I've always thought that this design and the similar Java uses with interfaces, anonymous classes and adapters is just a design chosen because the languages are limited, don't support delegates.
Indeed. Well, I think you could use runtime-reflection to achieve the same thing in Java, but that would make your interfaces "informal", in the sense that you just can't put those optional functions in the interfaces. (And it'd uglify the code a lot.) Technically, this is what was done prior Mac OS X 10.6 with informal protocols; I say "almost" since the method signatures being added as unimplemented categories to NSObject, they could still checked for correctness by the compiler.
If I recall correctly writing code that uses runtime-refelection in Java isn't pretty, especial compared to something like Ruby where it's just as easy as any other code you write.
 Perhaps interfaces could be allowed to have optional methods that would
 require you to check if they're implemented before use.
How would you check if a method is implemented or not ?
In Objective-C, it's quite easy. For instance, I've made my own subclass of NSTableView querying the delegate for a background color for a given row. So here's how you use the delegate: id delegate = [self delegate]; if ([delegate respondsToSelector: selector(tableView:backgroundColorForRow:)]) { NSColor *color = [delegate tableView:self backgroundColorForRow:row]; ...draw background color... }
Yes, Objective-C makes that easy with its message passing system.
 In D you can't do that currently, but here's how it could be added.
 First add an optional attribute for interface members:

 interface TableDelegate {
 ...
  optional NSColor backgroundColorForRow(NSTableView table, size_t row);
 }

 Classes implementing the interface can omit the  optional methods. When
 omitted, the interface function is null. When using the interface, you
 must check first if the optional function is implemented by making sure
 the address isn't null:

 TableDelegate delegate = this.delegate;

 if (&delegate.backgroundColorForRow != null) {
 NSColor color = delegate.backgroundColorForRow(this, row);
 ...draw background color...
 }

 This would work somewhat similarily to weak-linked symbols.
That is quite a clever idea, to check for null. The question is then how much trouble would that be to implement. -- /Jacob Carlborg
May 26 2010
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-05-26 15:44:58 -0400, Jacob Carlborg <doob me.com> said:

 On 2010-05-26 16.20, Michel Fortin wrote:
 
 In D you can't do that currently, but here's how it could be added.
 First add an optional attribute for interface members:
 
 interface TableDelegate {
 ...
  optional NSColor backgroundColorForRow(NSTableView table, size_t row);
 }
 
 Classes implementing the interface can omit the  optional methods. When
 omitted, the interface function is null. When using the interface, you
 must check first if the optional function is implemented by making sure
 the address isn't null:
 
 TableDelegate delegate = this.delegate;
 
 if (&delegate.backgroundColorForRow != null) {
 NSColor color = delegate.backgroundColorForRow(this, row);
 ...draw background color...
 }
 
 This would work somewhat similarily to weak-linked symbols.
That is quite a clever idea, to check for null. The question is then how much trouble would that be to implement.
If I had to guess those things would need to be done: 1. Add the " optional" attribute token to the lexer 2. Allow optional to be attached to functions in an interface 3. When checking if a class correctly implements an interface, allow unimplemented optional functions 4. When generating the interface's vtable, use null for unimplemented optional functions I think that's all. Checking for null is easily done by retrieving the delegate pointer (which you can already do), and calling the function uses the usual calling mechanism for interfaces. I expect that trying to call an unimplemented interface function would be like trying to call a null function pointer. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 26 2010
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Steven Schveighoffer wrote:
 I'd ask the naysayers of interfaces for dcollections, and also the 
 supporters: what is the point of having interfaces in D?  Are interfaces 
 pretty much obsolete, and I am just nostalgic about their utility?
Interfaces are for runtime polymorphism, rather than compile time polymorphism. They are especially useful for things like: 1. runtime plugin interfaces 2. designs where strict implementation hiding is desired 3. to have binary libraries (shared and static) 5. reduced code memory footprint 6. experience shows they are an excellent fit for user interfaces
7. Compiler-assisted verification.
 Compile time polymorphism, such as what templates provide, are most useful for:
 
 1. maximum performance
 2. minimal data memory consumption
 3. better compile time checking
 
 
 I believe the tradeoffs for collection types favor compile time polymorphism 
 because:
 
 1. performance is often critical for collections
 2. C++ STL has shown the success of this approach
 3. collections must fit in naturally with ranges, and ranges are compile time 
 polymorphic
How does error message quality compare between failing to conform to an interface verse conforming to an isXXX template? Anything that increases the time gap between code writing and code verification is bad. Does Phobos consistently use an isXXX template-based self-check that other writers of ranges can copy? Certainly, inheritance syntax is easier in this regard.
May 24 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 7. Compiler-assisted verification.
For interfaces, the compile time checking is limited to verifying that functions with the right signature are supplied. Templates can go considerably beyond that with the constraint checking.
May 24 2010
parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Jason House wrote:
 7. Compiler-assisted verification.
For interfaces, the compile time checking is limited to verifying that functions with the right signature are supplied. Templates can go considerably beyond that with the constraint checking.
constraints are more powerful, but they have downsides: • If a class is incorrectly defined, failure to use a type without a constraint check leads to errors in the code using it instead of the class definition. Usage isn't always guaranteed to be correct either, so the developer must spend extra time diagnosing the real error. • If a class is incorrectly, initial usage without a constraint may completely miss the error. Easy examples would be a typo propogated with copy/paste, or neglecting to use save. • If a class is incorrectly defined and usage uses a constraint, the developer will simply get an error that there is no matching call. • If a constraint is incorrectly defined and usage uses the constraint, the developer will simply get an error that there is no matching call. None of these scenarios are particularly helpful for a developer creating/expanding a family of objects. PS: The use of the word class above is for clarity instead of using type or object. I could have said struct as well.
May 25 2010
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 25 May 2010 09:03:34 -0400, Jason House  
<jason.james.house gmail.com> wrote:

 Walter Bright Wrote:

 Jason House wrote:
 7. Compiler-assisted verification.
For interfaces, the compile time checking is limited to verifying that functions with the right signature are supplied. Templates can go considerably beyond that with the constraint checking.
constraints are more powerful, but they have downsides: • If a class is incorrectly defined, failure to use a type without a constraint check leads to errors in the code using it instead of the class definition. Usage isn't always guaranteed to be correct either, so the developer must spend extra time diagnosing the real error. • If a class is incorrectly, initial usage without a constraint may completely miss the error. Easy examples would be a typo propogated with copy/paste, or neglecting to use save. • If a class is incorrectly defined and usage uses a constraint, the developer will simply get an error that there is no matching call. • If a constraint is incorrectly defined and usage uses the constraint, the developer will simply get an error that there is no matching call. None of these scenarios are particularly helpful for a developer creating/expanding a family of objects.
These all boil down to the fact that you must declare an interface up front, whereas constraints are not required. You can get to about the same level as an interface by using static asserts, but this is optional (it probably should be a "best practice" though somewhere). -Steve
May 25 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 Walter Bright Wrote:
 
 Jason House wrote:
 7. Compiler-assisted verification.
For interfaces, the compile time checking is limited to verifying that functions with the right signature are supplied. Templates can go considerably beyond that with the constraint checking.
constraints are more powerful, but they have downsides: • If a class is incorrectly defined, failure to use a type without a constraint check leads to errors in the code using it instead of the class definition. Usage isn't always guaranteed to be correct either, so the developer must spend extra time diagnosing the real error. • If a class is incorrectly, initial usage without a constraint may completely miss the error. Easy examples would be a typo propogated with copy/paste, or neglecting to use save. • If a class is incorrectly defined and usage uses a constraint, the developer will simply get an error that there is no matching call. • If a constraint is incorrectly defined and usage uses the constraint, the developer will simply get an error that there is no matching call. None of these scenarios are particularly helpful for a developer creating/expanding a family of objects.
You can also make constraints that give custom error messages, so you can do better than the compiler's stab at it. How good they are is up to the designer of the type.
May 25 2010
parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Jason House wrote:
 Walter Bright Wrote:
 
 Jason House wrote:
 7. Compiler-assisted verification.
For interfaces, the compile time checking is limited to verifying that functions with the right signature are supplied. Templates can go considerably beyond that with the constraint checking.
constraints are more powerful, but they have downsides: • If a class is incorrectly defined, failure to use a type without a constraint check leads to errors in the code using it instead of the class definition. Usage isn't always guaranteed to be correct either, so the developer must spend extra time diagnosing the real error. • If a class is incorrectly, initial usage without a constraint may completely miss the error. Easy examples would be a typo propogated with copy/paste, or neglecting to use save. • If a class is incorrectly defined and usage uses a constraint, the developer will simply get an error that there is no matching call. • If a constraint is incorrectly defined and usage uses the constraint, the developer will simply get an error that there is no matching call. None of these scenarios are particularly helpful for a developer creating/expanding a family of objects.
You can also make constraints that give custom error messages, so you can do better than the compiler's stab at it. How good they are is up to the designer of the type.
So you're favoring an isXXX as well as a requireXXX? Such duplication is both annoying and error prone. Making isXXX use requieXXX under the hood ishigher implementation complexity. I don't know about others, but I was drawn to its simplicity over C++. Many have praised its ability to make template programming simple. This feels like a step backwards.
May 26 2010
parent Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 So you're favoring an isXXX as well as a requireXXX?  Such duplication is
 both annoying and error prone. Making isXXX use requieXXX under the hood
 ishigher implementation complexity. I don't know about others, but I was
 drawn to its simplicity over C++. Many have praised its ability to make
 template programming simple. This feels like a step backwards.
In C++, the concepts design died. There's nothing you can do in C++ about the bad error messages. Furthermore, compile time error checking in C++ is limited to types. If something cannot be expressed as a distinct type, it cannot be checked in C++. (BTW, interfaces in D have the same problem, you can only check the type with them. With templates, you can check behavior and other characteristics.) The fundamental problem is a compiler can only see a worm's eye view of what a component is supposed to do. There's no algorithm in the world that can take a block of code and have the compiler figure out that this is a hash map collection class, and give lucid error messages in those terms rather than low level operations. As a component designer, however, D gives you the ability to detect usage errors at compile time in ways that go far beyond just type errors, and inform the user about them in high level terms. You're right in that D doesn't do this automatically, just like a browser cannot automatically make attractive web pages. You as the component designer have to write the code to make it work the way you want.
May 26 2010
prev sibling next sibling parent reply Marianne Gagnon <auria.mg gmail.com> writes:
In my experience (not related to DCollections), having interfaces is useful to
ensure reduced coupling, thus enabling the use of mock classes for unit tests
(or simply to test your module, when your module needs to use services provided
by another module that is being written by a colleague but not yet usable,
etc...)

 A long discussion on the utility of interfaces has taken place on the  
 announce newsgroup following my announcement of dcollections for D2.
 
 Dcollections supports interfaces peripherally.  Meaning the types in  
 dcollections are essentially concrete classes with interfaces tacked on  
 for cases where interfaces make sense.
 
 Whenever I've used dcollections (D1 included) I don't think I've ever used  
 the interface for a container, only the concrete type.  But within  
 dcollections, there's some functions that use the interface type to do  
 (IMO) nifty things like compare two sets that have different  
 implementations.
 
 The general consensus is that interfaces for dcollections do not add any  
 meaningful value, and that I should do everything with generic programming  
 and templates.  My view is that interfaces are useful for binary  
 compatibility and in reducing the footprint of executables.  But that  
 doesn't mean much at the moment, because D is statically linked.
 
 I'd ask the naysayers of interfaces for dcollections, and also the  
 supporters: what is the point of having interfaces in D?  Are interfaces  
 pretty much obsolete, and I am just nostalgic about their utility?
 
 What do you think?
 
 -Steve
May 24 2010
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
On 24/05/2010 22:14, Marianne Gagnon wrote:
 In my experience (not related to DCollections), having interfaces is useful to
ensure reduced coupling, thus enabling the use of mock classes for unit tests
(or simply to test your module, when your module needs to use services provided
by another module that is being written by a colleague but not yet usable,
etc...)
That's generally true, but I don't think it applies to DCollections. Creating mock objects is for mocking complex subsystems and modules, such that setting up (and later verifying) the mock object is much easier than if the true object/subsystem was used. But that would not apply to a collection object, since a mock collection would not really be easier to work with than with the real collection object. -- Bruno Medeiros - Software Engineer
May 26 2010
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:

class A {}
class B {}
And two collections Collection<A> and Collection<B> it's possible to concat
them into an array A[]. The efficient way to do it is to use CopyTo(T[],int)
method, but it accepts only array of exact collection item's type, so I had to
change the Collection<B> type to Collection<A>.
May 24 2010
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 25 May 2010 02:26:02 -0400, Kagamin <spam here.lot> wrote:


 class A {}
 class B {}
 And two collections Collection<A> and Collection<B> it's possible to  
 concat them into an array A[]. The efficient way to do it is to use  
 CopyTo(T[],int) method, but it accepts only array of exact collection  
 item's type, so I had to change the Collection<B> type to Collection<A>.
Did you mean class B : A {} ? According to this page, it says that an exception could be thrown if "Type T cannot be cast automatically to the type of the destination array." This implies that a destination array type to which T could automatically be cast should work. http://msdn.microsoft.com/en-us/library/0efx51xw%28v=VS.100%29.aspx But the larger point that compile-time code generation can help with this is valid. -Steve
May 25 2010
prev sibling parent reply Kagamin <spam here.lot> writes:
Steven Schveighoffer Wrote:

 On Tue, 25 May 2010 02:26:02 -0400, Kagamin <spam here.lot> wrote:
 

 class A {}
 class B {}
 And two collections Collection<A> and Collection<B> it's possible to  
 concat them into an array A[]. The efficient way to do it is to use  
 CopyTo(T[],int) method, but it accepts only array of exact collection  
 item's type, so I had to change the Collection<B> type to Collection<A>.
Did you mean class B : A {} ?
Ah, yes.
 According to this page, it says that an exception could be thrown if "Type  
 T cannot be cast automatically to the type of the destination array."   
 This implies that a destination array type to which T could automatically  
 be cast should work.
ICollection<A> acoll; ICollection<B> bcoll; A[] cat; ICollection<A>.CopyTo(A[],int) ICollection<B>.CopyTo(B[],int) - note the signature, the destination array can't be an array of supertype. There's no chance to throw an exception because the code doesn't pass type check at compile time.
May 27 2010
parent Kagamin <spam here.lot> writes:
 ICollection<A> acoll;
 ICollection<B> bcoll;
 A[] cat;
 ICollection<A>.CopyTo(A[],int)
 ICollection<B>.CopyTo(B[],int) - note the signature, the destination array
can't be an array of supertype. There's no chance to throw an exception because
the code doesn't pass type check at compile time.
To ease understanding: ICollection<Animal> acoll; ICollection<Cat> ccoll; Animal[] animals; ICollection<Animal>.CopyTo(Animal[],int) ICollection<Cat>.CopyTo(Cat[],int)
May 27 2010