July 31, 2015
On Friday, 31 July 2015 at 11:16:48 UTC, Biotronic wrote:
> 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))
> { /* ... */ }

That would be better !
July 31, 2015
On Thursday, 30 July 2015 at 19:12:27 UTC, jmh530 wrote:
> On Thursday, 30 July 2015 at 10:40:59 UTC, Atila Neves wrote:
>> [...]
>
> 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.

Check the out the PR I mentioned:

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

The idea would be to define a similar check function for each constraint. Without it, you don't get the nice error messages.

Atila
July 31, 2015
On Friday, 31 July 2015 at 11:16:48 UTC, Biotronic wrote:
> 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))
> { /* ... */ }

Because you want to:

1. Check your type and get sensible error messages
2. Use a constraint in function declarations

You can't have `@satisfies!(isInputRange, T)` give you an error message if it doesn't have access to the source code of the lambda that actually does the checking and you can't get error messages unless you force the compilation of code you _know_ won't compile.

It's ok for template constraints to be false without causing a compilation error; that's how we get compile-time template function resolution. When you're using something like `@satisfies` however, you want a compilation error and its message. That's why you need two things.

Atila
July 31, 2015
On Friday, 31 July 2015 at 14:23:28 UTC, Atila Neves wrote:
> On Friday, 31 July 2015 at 11:16:48 UTC, Biotronic wrote:
>> On Thursday, 30 July 2015 at 10:40:59 UTC, Atila Neves wrote:
>>> [...]
>>
>> 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))
>> { /* ... */ }
>
> Because you want to:
>
> 1. Check your type and get sensible error messages
> 2. Use a constraint in function declarations
>
> You can't have `@satisfies!(isInputRange, T)` give you an error message if it doesn't have access to the source code of the lambda that actually does the checking and you can't get error messages unless you force the compilation of code you _know_ won't compile.
>
> It's ok for template constraints to be false without causing a compilation error; that's how we get compile-time template function resolution. When you're using something like `@satisfies` however, you want a compilation error and its message. That's why you need two things.
>
> Atila

Notice however, that (again, in the PR), the "real" definition happens only once, in the check function. Then the constraint is always a trivial `enum MyConstraint = is(typeof(myCheckFunction));`. It's just a matter of moving the lambda away, giving it a name, and doing the weird `if(!__ctfe)` hack.

Atila
July 31, 2015
On Friday, 31 July 2015 at 14:23:28 UTC, Atila Neves wrote:
> On Friday, 31 July 2015 at 11:16:48 UTC, Biotronic wrote:
>> 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))
>> { /* ... */ }
>
> Because you want to:
>
> 1. Check your type and get sensible error messages
> 2. Use a constraint in function declarations
>
> It's ok for template constraints to be false without causing a compilation error; that's how we get compile-time template function resolution. When you're using something like `@satisfies` however, you want a compilation error and its message. That's why you need two things.
>
> Atila

He does have a point. isInputRange!Bar = (checkInputRange!Bar == true);
July 31, 2015
On Friday, 31 July 2015 at 14:24:58 UTC, Atila Neves wrote:
>
> Notice however, that (again, in the PR), the "real" definition happens only once, in the check function. Then the constraint is always a trivial `enum MyConstraint = is(typeof(myCheckFunction));`. It's just a matter of moving the lambda away, giving it a name, and doing the weird `if(!__ctfe)` hack.
>
> Atila

Looking at the PR also resolved my earlier question. Running the code as below (do not import std.range) will tell you exactly what isn't implemented from isInputRange (in this case, I commented out popFront). Very cool.

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)"
}

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

bool checkInputRange(R)(inout int = 0)
{
    if (__ctfe)
    {
        R r = R.init;     // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }
    return true;
}

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

void main()
{
	Zeroes Z;
}

August 01, 2015
On Friday, 31 July 2015 at 16:28:30 UTC, jmh530 wrote:
> Looking at the PR also resolved my earlier question. Running the code as below (do not import std.range) will tell you exactly what isn't implemented from isInputRange (in this case, I commented out popFront). Very cool.
>
> 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)"
> }
>
> template isInputRange(R)
> {
>     enum bool isInputRange = is(typeof(checkInputRange!R));
> }
>
> bool checkInputRange(R)(inout int = 0)
> {
>     if (__ctfe)
>     {
>         R r = R.init;     // can define a range object
>         if (r.empty) {}   // can test for empty
>         r.popFront();     // can invoke popFront()
>         auto h = r.front; // can get the front of the range
>     }
>     return true;
> }
>
> @satisfies!(isInputRange, Zeroes)
> struct Zeroes {
>     enum empty = false;
>     //void popFront() {}
>     @property int front() { return 0; }
> }
>
> void main()
> {
> 	Zeroes Z;
> }

Nice. Here are the actual error messages for the record:

/d125/f236.d(18): Error: no property 'popFront' for type 'Zeroes'
/d125/f236.d(24): Error: template instance f236.satisfies!(isInputRange, Zeroes) error instantiating

Piotrek



1 2 3
Next ›   Last »