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