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

cym13 via Digitalmars-d digitalmars-d at puremagic.com
Fri Jul 31 04:31:23 PDT 2015


On Thursday, 30 July 2015 at 10:40:59 UTC, Atila Neves wrote:
> On Wednesday, 29 July 2015 at 20:41:02 UTC, Tofu Ninja wrote:
>> On Wednesday, 29 July 2015 at 20:26:53 UTC, Tofu Ninja wrote:
>>> If you write:
>>>
>>> @satisfies!(isInputRange, MyRange) struct MyRange { ... }
>>>
>>> the UDA can check it self, it really works as expected. Which 
>>> is why I suggested a way to get whatever the UDA is attached 
>>> to automatically in my other post.
>>
>> A UDA can reference the thing it's being attached to with no 
>> problems.
>> For example, this works 100% as expected right now...
>>
>> @UDA!testS struct testS // UDA fails to instantiate because 
>> testS is not an inputRange!
>> {
>> 	
>> }
>>
>> template UDA(alias a)
>> {
>> 	import std.range;
>> 	static assert(isInputRange!a);
>> }
>>
>> The only thing that would be needed to make this a nice 
>> solution is some syntax sugar to automatically get whatever 
>> the UDA is attached to, which is why I suggested this:
>>
>> template UDA(alias a = __UDA_ATTACHMENT__) { ... }
>
> You still wouldn't get a better error message here than with:
>
> struct MyRange {
>     ...
>     static assert(isInputRange!MyRange);
> }
>
> It's less typing, but you still wouldn't know why the static 
> assertion failed. Now, if we make Adam's idiom in my 
> aforementioned pull request the go-to thing, then this would be 
> good:
>
> @satisfies!(checkInputRange, MyRange) struct MyRange { ... }
>
> There's still the problem of having two names for each 
> constraint: checkInputRange and isInputRange. But... we could 
> also establish the convention of checkXXX and use string mixins 
> to turn this:
>
> @satifies!(isInputRange, MyRange)
>
> into (which works cos I just tried it):
>
> template satisfies(alias Constraint, R) {
>     enum check = "check" ~ Constraint.stringof[2..$-3] ~ "!(R)";
>     enum assert_ = "static assert("~ check ~ ");";
>     mixin(assert_); //mixes in "static 
> assert(checkInputRange!R)"
> }
>
>
> @satisfies!(isInputRange, Zeroes)
> struct Zeroes {
>     enum empty = false;
>     void popFront() {}
>     @property int front() { return 0; }
> }
>
>
> Now, _this_ I could go for.
>
> Atila

The one thing I'm dissatisfied with this proposition is 
“isInputRange”. Implementing it requires checking that the struct 
has some methods, and checking that means that “isInputRange” has 
the information about what methods it should have, and the name 
suggests that it is done imperatively. Don't we already have 
something to describe what methods an (conceptual) object should 
have? Yes we do, that's interfaces and they are declarative which 
is nice. They are great as reference and for documentation, 
better than a succession of “static if”. The problem with 
interfaces is that one has to inherit from them.

I think “isInputRange” in itself is a bad idea hardly reusable. 
What I would like to see is a way to take an interface and 
implicitely turn it into an interface checker that would 
statically check a struct. I'd like to turn:

   @satisfies!(isInputRange, Zeroes)
   struct Zeroes {
       enum empty = false;
       void popFront() {}
       @property int front() { return 0; }
   }

into

   @satisfies!(InputRangeInterface /*whatever the name */, Zeroes)
   struct Zeroes {
       enum empty = false;
       void popFront() {}
       @property int front() { return 0; }
   }

That would be great to get people to properly document their 
interfaces (good for documentation) without the drawback of 
having to inherit from them, while still being available for 
normal interfaces and without any information redundancy.

However, I'm not sure that's possible today... What do you think 
about it?


More information about the Digitalmars-d mailing list