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

Atila Neves via Digitalmars-d digitalmars-d at puremagic.com
Tue Jul 28 05:49:15 PDT 2015


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


More information about the Digitalmars-d mailing list