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