Andrei's interface requests

Denis Koroskin 2korden at gmail.com
Sat Apr 4 07:41:10 PDT 2009


On Sat, 04 Apr 2009 01:13:09 +0400, Andrei Alexandrescu <SeeWebsiteForEmail at erdani.org> wrote:

> Christopher Wright wrote:
>> Andrei has put up a few enhancement requests in the bugzilla:
>> http://d.puremagic.com/issues/show_bug.cgi?id=2784
>> http://d.puremagic.com/issues/show_bug.cgi?id=2785
>> http://d.puremagic.com/issues/show_bug.cgi?id=2786
>
> Yah, this too:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=2050
>
>> These are intended to change interfaces from being solely a tool of  
>> polymorphism into a way of specifying type constraints. The additions  
>> recommended can only be used at compile time (constructors, static  
>> functions, and nested types).
>
> Yah. Let me add that I haven't thought the changes through, but I posted  
> them to foster discussion, which is happening (thanks!).
>
>> I grant that it would be quicker and clearer to write:
>> interface Foo
>> {
>>    static void stuff();
>>    this (int);
>> }
>> template Template(Arg : Foo) {}
>>  than to write:
>> template Template(Arg) if (is (typeof (new Arg(0)) &&  
>> isStaticFunction!(Arg, "stuff")) {}
>
> Yah. Let me add that after the OOP meteoric rise in the past years, many  
> of us can instantly figure how the former works, whereas the latter is  
> thoroughly obscure.
>
> Let me also pull the definition of a couple of range types in std.range  
> (by the way, I should commit the new Phobos today):
>
> template isInputRange(R)
> {
>      enum bool isInputRange = is(typeof(
>      {
>          R r;             // can define a range object
>          if (r.empty) {}  // can test for empty
>          r.next;          // can invoke next
>          auto h = r.head; // can get the head of the range
>      }()));
> }
>
> I wrote it as clearly as I could, and it doesn't quite look that pretty.  
> I didn't even know the typeof({ ... }()) trick is possible; I saw it  
> somewhere on this group, and took to it. Otherwise the code would have  
> been even more convoluted (along the lines of what you wrote, just more  
> of it). Furthermore, if a type is not a range, there's no simple way to  
> output an error message telling exactly which test if failed. The only  
> test possible is e.g. isInputRange!MyType.
>
> I think we can't quite be happy that this is D's offering with regard to  
> concepts. On the other hand, we'd like to avoid the aggravation of C++  
> concepts, which, in my humble opinion, have a very low power/complexity  
> ratio. So I wanted to look at how interfaces can be reasonably extended  
> into expressing concepts while still being strongly integrated with  
> their classic runtime polymorphic role.
>
>> However, I'm not sure whether this would be used much at all, and it  
>> deserves some discussion.
>>  One detail of #2785 seems problematic:
>> interface Foo
>> {
>>     extern void bar(int, Foo, double);
>> }
>>  meaning that a non-member function bar should exist that accepts an  
>> int, *the implementor of Foo*, and a double.
>>  This is a huge and silent divergence from the standard meaning of  
>> using the interface name; it would allow:
>> class C : Foo
>> {
>>     static void bar(int, C, double);
>> }
>>  and disallow:
>> class C : Foo
>> {
>>     static void bar(int, Foo, double);
>> }
>>  Thoughts? Any concerns that I have not raised? I don't do sufficient  
>> metaprogramming to find any of this useful, I admit.
>
> I thought of that, too. One option is to parameterize Foo with its  
> subtype's name:
>
> interface Foo(X)
> {
>      extern void bar(int, X, double);
> }
>
> Then you'd require C : Foo!C and so on. I'm not sure how important this  
> detail is, but it is worth thinking of.
>
> Another issue is that constructors in a struct are one thing, whereas in  
> a class they are quite a different thing (references of a class can be  
> copied!)
>
>
> Andrei

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.




More information about the Digitalmars-d mailing list