Interfaces, traits, concepts, and my idea for a DIP

Daniel Kozák via Digitalmars-d digitalmars-d at puremagic.com
Tue Jul 28 06:23:36 PDT 2015


On Tue, 28 Jul 2015 12:49:15 +0000
"Atila Neves" <atila.neves at gmail.com> wrote:

> So I missed the boat on the lengthy Rust traits discussion on the 
> other thread. I confess I didn't have time to read all of it so 
> forgive me if I bring up something that's already been said there.
> 
> Since last year, after having written a ton of generic code with 
> compile-time reflection and (although it didn't have a name at 
> the time) design by introspection, I got pretty annoyed at having 
> to debug why my template constraints failed. So I had an idea and 
> it's been waiting for me to write a DIP ever since. I don't think 
> I have time right now to write the DIP properly, but given the 
> recent discussion, here's a sketch. Say I have:
> 
> void func(R)(R range) if(isInputRange!R) { ... }
> 
> Maybe I didn't even write the function, maybe it's in Phobos. But 
> I'm interested in implementing an input range but this fails to 
> instantiate. I have no idea why. Now, it's usually good practice 
> to include a `static assert(isInputRange!MyType)` somewhere, but 
> if that fails... you have no idea why. Was it `popFront`? 
> `front`? `empty`? Why didn't it compile?
> 
> I've resorted to copying the code from the lambda in the 
> customary `is(typeof({...}))` in the template constraint and 
> forcing the compiler to give the message that it's hiding from 
> me. The compiler knows why the static assert failed, but it won't 
> tell me unless I force it to.
> 
> This is analogous to dynamic dispatch except, in that case, the 
> compiler will quite gladly tell you if you forgot to implement a 
> virtual function and much more besides. And why can it do that? 
> Because you told it your class implements an interface or 
> inherits from a class with abstract functions / methods.
> 
> So... instead of having traits / concepts, what I wanted from D 
> is to be able to do this:
> 
> struct MyRange: isInputRange { ... }
> 
> or
> 
> struct MyRange: static isInputRange { ... } // that way classes 
> could do this too
> 
> Then instead of:
> 
> foo.d(9): Error: static assert  (isInputRange!(MyRange)) is false
> 
> I'd get:
> 
> foo.d(9): Error: MyRange does not implement popFront
> 
> 
> I'd hoped to paste the result of trying to compile isInputRange 
> in the 2nd example but apparently right now I get the very 
> horrible:
> 
> foo.d(17): Error: template std.range.primitives.popFront cannot 
> deduce function from argument types !()(MyRange), candidates are:
> /usr/include/dlang/dmd/std/range/primitives.d(1992):        
> std.range.primitives.popFront(T)(ref T[] a) if 
> (!isNarrowString!(T[]) && !is(T[] == void[]))
> /usr/include/dlang/dmd/std/range/primitives.d(2015):        
> std.range.primitives.popFront(C)(ref C[] str) if 
> (isNarrowString!(C[]))
> foo.d(22): Error: template instance foo.func!(MyRange) error 
> instantiating
> 
> 
> 
> At least it mentions `popFront`. Anyway, you understand what I 
> want from this. The only I think I've seen that comes close to 
> achieving this is a massive hack abusing __ctfe in Adam's book.
> 
> I've lost count of how many times a template constraint failed 
> because of some stupid thing like @safe functions not being able 
> to call @system ones and it was really hard figuring out why.
> 
> I don't want C++ concepts. I just want compiler error messages 
> pointing me in the general direction of why my type fails a 
> template constraint. This is especially bad when there are 
> several overloads with different constraints, I usually have to 
> comment out code until the offending one presents itself.
> 
> Atila


I was thinking about same many times before. With one difference:

instead of this:
struct MyRange: isInputRange { ... }

I would use something like this:

@interface(InputRange, ...)
struct MyRange { ... }


@interface(InputRange, ...)
class MyClassRange { ... }





More information about the Digitalmars-d mailing list