July 29, 2015
On Wednesday, 29 July 2015 at 08:25:04 UTC, Roland Hadinger wrote:
> On Tuesday, 28 July 2015 at 12:49:17 UTC, Atila Neves wrote:
>> So... instead of having traits / concepts, what I wanted from D is to be able to do this:
>>
>> struct MyRange: isInputRange { ... }
>
> +1
>
>> or
>>
>> struct MyRange: static isInputRange { ... } // that way classes could do this too
>
> What about this instead:
>
>     @satisfies(isInputRange) struct MyRange { ... }
>
> which is not as terse, but maybe less confusing, because intuitively ':' could be mistaken to mean 'extends'.
>
> 'static' has too many meanings already for my taste. I really don't like it when frequently used keywords are reused to mean different things in slightly different places.

That looks nice, but who's going to check it? UDAs have to be reflected on to well, do anything. At least a template mixin will cause a static assert to fail.

Atila
July 29, 2015
On Tuesday, 28 July 2015 at 12:49:17 UTC, Atila Neves 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.
>
> [...]

I went back to my copy of Adam's book and made a PR based on it:

https://github.com/D-Programming-Language/phobos/pull/3520

Doesn't require a language change and way better than the status quo. But still hacky.

Atila
July 29, 2015
On Wednesday, 29 July 2015 at 14:51:52 UTC, Atila Neves wrote:
> On Wednesday, 29 July 2015 at 08:25:04 UTC, Roland Hadinger wrote:
>> On Tuesday, 28 July 2015 at 12:49:17 UTC, Atila Neves wrote:
>>> So... instead of having traits / concepts, what I wanted from D is to be able to do this:
>>>
>>> struct MyRange: isInputRange { ... }
>>
>> +1
>>
>>> or
>>>
>>> struct MyRange: static isInputRange { ... } // that way classes could do this too
>>
>> What about this instead:
>>
>>     @satisfies(isInputRange) struct MyRange { ... }
>>
>> which is not as terse, but maybe less confusing, because intuitively ':' could be mistaken to mean 'extends'.
>>
>> 'static' has too many meanings already for my taste. I really don't like it when frequently used keywords are reused to mean different things in slightly different places.
>
> That looks nice, but who's going to check it? UDAs have to be reflected on to well, do anything. At least a template mixin will cause a static assert to fail.
>
> Atila

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.
July 29, 2015
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__) { ... }
July 29, 2015
On 7/29/15 4:25 AM, Roland Hadinger wrote:
>      @satisfies(isInputRange) struct MyRange { ... }

This is straight from the "creative use of what we have" mantra that I think is worth exploring. -- Andrei
July 30, 2015
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
July 30, 2015
On Thursday, 30 July 2015 at 10:40:59 UTC, Atila Neves wrote:
>
> 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

I might be overthinking this.

You would still need to define something for checkInputRange. Are you thinking something simple like

template checkInputRange(R)
{
    enum bool checkInputRange = isInputRange!R;
}

In this case, the error message is still referring to checkInputRange instead of isInputRange. So the user would still need to refer to that and see what that functions conditions are. Alternately, you could write checkInputRange to provide the user information about which functions are not implemented (like no pop, no popFront, no empty). That seems more difficult, but would be convenient for the user.

The benefit of something simple like

template satisfies_alt(alias Constraint, R) {
    static assert(Constraint!R);
}

is that at least you don't have to write extra functions. You only need isInputRange. The downside is that the error message references Constraint instead of isInputRange. That's less intuitive.
July 31, 2015
On Tuesday, 28 July 2015 at 18:23:02 UTC, Tofu Ninja wrote:
> On Tuesday, 28 July 2015 at 13:23:37 UTC, Daniel Kozák wrote:
>> [...]
>
> I have actually thought about this as well, and a thing that could actually make this possible is if UDAs could get the thing they are attached to. I realized it could be done easily when I saw that this worked:
>
> [...]

+1

automatic UDA attachment is something I've wanted ever since i started using D.
and their would be other uses for automatic UDA attachment than for testing structs.
July 31, 2015
On Thursday, 30 July 2015 at 10:40:59 UTC, Atila Neves wrote:
> 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

Why are there two different things in the first place?

@satisfies!(myConcept, T) should test the constraints and give a sensible error message. This you use to indicate that type T implements myConcept.

Why can't another template use the very same concept information to check if a type implements the concept?

e.g.:

@satisfies!(myConcept, MyStruct)
struct MyStruct { /* ... */ }

void foo(T)(T t) if (check!(myConcept, T))
{ /* ... */ }
July 31, 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?