April 04, 2009
2784: interface Foo { typedef Bar; }
- confused me for a while, so a clearer description might be nice
-- i.e. clarify that something like 'new I.bar()' is invalid.
- seems only to be useful as part of the other contract-like interface enhancements
-- Like 2786 & 2785 below, this creates a difference between normal interface members and typedefs, which I don't like.

2785: interface Foo{ extern void bar(int, Foo, double);
- I like the concept of extern functions in this concept, though it does rise questions of what scopes/modules are used for match searching.
- The use of Foo to mean the class name seems like the wrong keyword.
-- What if I truly wanted a function that took a Foo?
-- This makes these function definitions behave differently than definitions outside the interface. Perhaps 'typeof(this)'
- Like 2786 below, this creates a difference between normal interface members and externs, which I don't like.

2786: Interfaces should be able to require constructors
- Seems like a decent idea.
- But, this creates a difference between normal interface members and the constructor, which I don't like:
-- the implementors                    have a ___     // constructor
-- the implementors and the interface  have a ___     // everything else

2050: Interfaces with static/final methods with method bodies
- I like the idea of defining that an interface implementor should have static or final methods.
- However, there seem to be some major composability issues, with implementing two interfaces with identical final / static methods.
-- i.e. this is the classic diamond problem of multiple inheritance, just with functions instead of functions and data.
-- This also applies to overloading an interface's static or final method. Is it possible? What happens when the class is accessed through the interface?
- How is this superior to the current interface + mixin approach?
-- Or an mixin-able interface approach? i.e. mixin myInterface; // Add myInterface's method bodies to a class

Regarding Interfaces and Concepts
Though struct interfaces, etc would be very nice, couldn't compile time reflection be used to create a Duck/Concept-type template constraint:
template void foo(T)(T x, T y)
    if( isDuck!(T,InputRange) ) {...}

Summary
Essentially all these enhancements would create a major difference between an interface and its implementor, which I think is a Bad Idea(tm). And the problem its trying to solve (i.e. c++ Concepts) is probably better solved with a general compile-time reflection template, than by altering interfaces.


April 04, 2009
Kagamin wrote:
> Andrei Alexandrescu Wrote:
> 
>>> 1. If template constraints suck, why not fixing *them*?
>> That's a fix.
> 
> This is a fix to interfaces, not to template constraints. If template constraints fail to serve their purpose, what are they for? Make them work, not others to work for them.

This is a change to interfaces to allow them to be used in place of template constraints in many circumstances. The idea is that the syntax of template constraints is sufficiently obtuse that you need to use idioms to get around it:

template AcceptableToTemplate(T)
{
	static if (...) // check for non-existence of property 1
	{
		alias void AcceptableToTemplate;
	}
	else static if (...) // check for non-existence of property 2
	{
		alias void AcceptableToTemplate;
	}
	else
	{
		alias T AcceptableToTemplate;
	}
}

template Template (T : AcceptableToTemplate!(T)) {}

Using that, it's difficult to determine what types you can pass to Template.
April 04, 2009
Denis Koroskin wrote:
> I'm not sure I understand everything you intend so lets clear things a little.
> 
> I agree there should be a way to enforce struct to implement some.. contract.
> But I believe you took a wrong way by using pseudo-interface inheritance.
> 
> First of all, it's called Concept in C++0x. I suggest to use the same name in D, too. (Yes, that's one more keyword, but this is just a discussion, right, and you didn't listen all my arguments yet).

Makes sense.

> concept Range(T)
> {
>    bool empty;
>    T value;
>    void next();
> }
> 
> I don't think "void next;" is okay, besides, it's always a function anyway! On the other side, I can image an infinite range that returns constant values and next is a do nothing function, but still you can't have a member of type void.

So far so good.

> Concept is different from interface. Concept says that if typeof(r) satisfies a Range concept, then the following syntax is allowed:
> 
> bool e = r.empty;
> T v = r.value;
> r.next();
> 
> But it doesn't say "how". r.empty may be a property, a (static) member, or even an enum. And that's exactly what's needed for Range definition!

Yah, but let me point out that interfaces can be also relaxed to require less conformance, just like you describe.

> On the contrary, interface defines a set of *virtual functions* that are invokable through an instance of that interface.

It does so currently. It doesn't mean the definition can't be improved, no?

> That's why I don't support an idea of the following:
> 
> interface Foo
> {
>    this(int i);
>    typedef Bar;
> }
> 
> Foo foo = new Foo(42); // huh?
> Foo.Bar x; //what's x.typeof?
> 
> BTW, why is(typeof(x)) is alowed but typeof(x) is not? I quite don't like x.typeof
> 
> Surely, you'll disallow both syntaxes, but this gets messy. Some of functions are callable through interface and some are only callable through a class that implements that interface!
> Once again, I state that you you mistakenly mix two different concepts (pun is not inteded).

I think this is really funny reading: http://www.thebestpageintheuniverse.net/c.cgi?u=puns It convinced me to avoid emphasizing or denying my puns :o).

Anyway, I agree with your point that uses of interface as concept may sometimes interfere with its uses in the traditional sense. One thing that comes to mind is:

interface Interface { void foo(); }
void foo(T : Interface)() { ... }
class C { void foo(); }

C does not explicitly declare abiding to Interface. However, structurally it does. Should foo be called? If interface is meant for binary binding, no. If interface is meant for concept checking, maybe. (The notion of "auto concept" in C++ is akin to this. There is currently a discussion on the C++ standardization mailing list on whether most concepts should be "auto" or not.)

> concept Foo
> {
>    int(int i);
>    typedef Bar; // fine
> }
> 
> Second, I believe structs *can* safely implement interfaces (hello, Weed!). They still shouldn't support inheritance, but *interface* inheritance:
> 
> interface Foo
> {
>    int bar();
> }
> 
> struct FooImpl : Foo
> {
>    int bar()
>    {
>        return 42;
>    }
> }
> 
> int acceptsFoo(Foo f)
> {
>    return f.bar();
> }
> 
> FooImpl fooImpl;
> acceptsFoo(fooImpl); // yes!
> 
> They'll get an implicit vtbl ptr, and since they don't support struct inheritance, no slicing is possible.
> 

I don't see enough good uses for this feature.


Andrei
April 04, 2009
On 04/04/2009 17:41, Denis Koroskin wrote:
> I'm not sure I understand everything you intend so lets clear things a
> little.
>
> I agree there should be a way to enforce struct to implement some..
> contract.
> But I believe you took a wrong way by using pseudo-interface inheritance.
>
> First of all, it's called Concept in C++0x. I suggest to use the same
> name in D, too. (Yes, that's one more keyword, but this is just a
> discussion, right, and you didn't listen all my arguments yet).
>
> concept Range(T)
> {
> bool empty;
> T value;
> void next();
> }
>
> I don't think "void next;" is okay, besides, it's always a function
> anyway! On the other side, I can image an infinite range that returns
> constant values and next is a do nothing function, but still you can't
> have a member of type void.
>
> Concept is different from interface. Concept says that if typeof(r)
> satisfies a Range concept, then the following syntax is allowed:
>
> bool e = r.empty;
> T v = r.value;
> r.next();
>
> But it doesn't say "how". r.empty may be a property, a (static) member,
> or even an enum. And that's exactly what's needed for Range definition!
>
> On the contrary, interface defines a set of *virtual functions* that are
> invokable through an instance of that interface. That's why I don't
> support an idea of the following:
>
> interface Foo
> {
> this(int i);
> typedef Bar;
> }
>
> Foo foo = new Foo(42); // huh?
> Foo.Bar x; //what's x.typeof?
>
> BTW, why is(typeof(x)) is alowed but typeof(x) is not? I quite don't
> like x.typeof
>
> Surely, you'll disallow both syntaxes, but this gets messy. Some of
> functions are callable through interface and some are only callable
> through a class that implements that interface!
> Once again, I state that you you mistakenly mix two different concepts
> (pun is not inteded).
>
> concept Foo
> {
> int(int i);
> typedef Bar; // fine
> }
>
> Second, I believe structs *can* safely implement interfaces (hello,
> Weed!). They still shouldn't support inheritance, but *interface*
> inheritance:
>
> interface Foo
> {
> int bar();
> }
>
> struct FooImpl : Foo
> {
> int bar()
> {
> return 42;
> }
> }
>
> int acceptsFoo(Foo f)
> {
> return f.bar();
> }
>
> FooImpl fooImpl;
> acceptsFoo(fooImpl); // yes!
>
> They'll get an implicit vtbl ptr, and since they don't support struct
> inheritance, no slicing is possible.
>

What you define is Concets *in C++* and interface *in Java*.
D doesn't have to use the same definitions.
D interfaces can represent abstract types. for example, say we have this interface:

interface I {
  this(int i);
  alias A;
  void funcA();
  final void funcB();
}

struct A : I {...}
class B : I {...}

why not make both A and B do exactly the same with I? meaning both inherit polymorphically funcA (it is in the virtual table for *both*) and statically at compile-time the compile time symbols like the alias A above.
IMO, this is more consistent. as you said above, there's nothing to prevent polymorphic struct inheritance of interfaces since it doesn't introduce the slicing problem.

regarding duct-typing: C++ needs this for compatibility with existing code. Why do you think that D needs to do the same choice? wouldn't it be more consistent if D would not provide two different semantics sets one for compile-time and one for run-time?
D will provide things like Ranges as interfaces in the stdlib, and any type that supports this interface should specify that.

from Andrei's post:

> interface Interface { void foo(); }
> void foo(T : Interface)() { ... }
> class C { void foo(); }
>
> C does not explicitly declare abiding to Interface. However, structurally it does. Should foo be called? If interface is meant for binary binding, no. If interface is meant for concept checking, maybe. (The notion of "auto concept" in C++ is akin to this. There is currently a discussion on the C++ standardization mailing list on whether most concepts should be "auto" or not.)

in the above case, I think I prefer the binary binding option.
April 04, 2009
Also, I don't understand why D needs a separate syntax for primitive types.
I don't like this whole "alias this" idea.
why can't we just use:

struct S : int { ... }

after all, that exactly what's that feature is trying to do, isn't it?
April 04, 2009
Yigal Chripun:
> Also, I don't understand why D needs a separate syntax for primitive types.

Generally D can work some more in the direction of unifying primitive types and objects. Some of it can be done with no performance price to pay.

Bye,
bearophile
April 04, 2009
bearophile wrote:
> Yigal Chripun:
>> Also, I don't understand why D needs a separate syntax for primitive types.
> 
> Generally D can work some more in the direction of unifying primitive types and objects. Some of it can be done with no performance price to pay.

Like which?

> Bye,
> bearophile
April 04, 2009
On Sat, 04 Apr 2009 15:38:03 -0400, grauzone <none@example.net> wrote:
> bearophile wrote:
>> Yigal Chripun:
>>> Also, I don't understand why D needs a separate syntax for primitive types.
>>  Generally D can work some more in the direction of unifying primitive types and objects. Some of it can be done with no performance price to pay.
>
> Like which?
>
>> Bye,
>> bearophile

One example is fixed length arrays, which only generate a field for array length when converted to dynamic arrays. Similarly, structs with interfaces would not need a pointer to their vtable in the struct, it (the pointer, not the vtable) would be created when converted to an interface.

1 2
Next ›   Last »