September 11, 2018
On Tuesday, 11 September 2018 at 13:08:46 UTC, Steven Schveighoffer wrote:
> On 9/10/18 7:00 PM, Nicholas Wilson wrote:
>> On Monday, 10 September 2018 at 20:44:46 UTC, Andrei Alexandrescu wrote:
>>> On 9/10/18 12:46 PM, Steven Schveighoffer wrote:
>>>> On 9/10/18 8:58 AM, Steven Schveighoffer wrote:
>>>>> I'll have to figure out why my specialized range doesn't allow splitting based on " ".
>>>>
>>>> And the answer is: I'm an idiot. Forgot to define empty :) Also my slicing operator accepted ints and not size_t.
>>>
>>> I guess a better error message would be in order.
>> 
>> https://github.com/dlang/DIPs/pull/131 will help narrow down the cause.
>
> While this would help eventually, I'd prefer something that just transforms all the existing code into useful error messages. See my response to Andrei.
>
> -Steve

Please tell me where to get one of those!

But yeah, that DIP will tell you that has slicing is you problem straight away. Extracting useful information to present to the user on why hasSlicing!R is false is much trickier for the same reason that providing useful information in the current template constraint format is hard: it is a bunch of potentially unstructured logic that has already been const-folded in order to evaluate it in the first place, so you can't re-evaluate it without flushing the template cache.

That's not to say that the situation can't be improved beyond what the DIP specifies, but I haven't had any brilliant ideas (and the Idea for that DIP was stolen from someone else anyway).
September 11, 2018
On Tuesday, 11 September 2018 at 02:00:29 UTC, Nicholas Wilson wrote:
> [snip]
>
> https://github.com/dlang/DIPs/pull/131 will help narrow down the cause.

I like it, but I worry people would find multiple ifs confusing.

The first line of the comment is about using static asserts and in contracts, but it looks like static asserts are allowed in in contracts for functions [1]. You can do the same thing in structs/classes with invariant blocks (but in contracts are not allowed). So basically, the same behavior for if can be reduced to in contracts with static asserts already. Multiple ifs would just be a slightly less verbose way to accomplish the same thing.

I suppose one issue might be that contracts are not compiled in during release mode, but I think release only impacts normal asserts, not static asserts.

Is there any reason why this is not sufficient?

[1] https://run.dlang.io/is/lu6nQ0
September 12, 2018
On 9/11/18 7:58 AM, jmh530 wrote:

> Is there any reason why this is not sufficient?
> 
> [1] https://run.dlang.io/is/lu6nQ0

That's OK if you are the only one defining S. But what if float is handled elsewhere?

-Steve
September 12, 2018
On Tuesday, 11 September 2018 at 14:58:21 UTC, jmh530 wrote:
> Is there any reason why this is not sufficient?
>
> [1] https://run.dlang.io/is/lu6nQ0

Overloads:

https://run.dlang.io/is/m5HGOh

The static asserts being in the constraint affects the template candidacy viability. Being in the function body/runtime contract does not so you'll end up with

onlineapp.d(17): Error: onlineapp.foo called with argument types (float) matches both:
onlineapp.d(1):     onlineapp.foo!float.foo(float x)
and:
onlineapp.d(7):     onlineapp.foo!float.foo(float x)

despite the fact only one of them is viable, whereas bar is fine.
September 12, 2018
On Wednesday, 12 September 2018 at 12:45:15 UTC, Nicholas Wilson wrote:
> 
>
> Overloads:
>
> [snip]

Good point.
September 13, 2018
On 09/11/2018 09:06 AM, Steven Schveighoffer wrote:
> 
> Then I found the true culprit was isForwardRange!R. This led me to requestion my sanity, and finally realized I forgot the empty function.

This is one reason template-based interfaces like ranges should be required to declare themselves as deliberately implementing said interface. Sure, we can tell people they should always `static assert(isForwardRage!MyType)`, but that's coding by convention and clearly isn't always going to happen.
September 13, 2018
On Thu, Sep 13, 2018 at 06:32:54PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
> On 09/11/2018 09:06 AM, Steven Schveighoffer wrote:
> > 
> > Then I found the true culprit was isForwardRange!R. This led me to requestion my sanity, and finally realized I forgot the empty function.
> 
> This is one reason template-based interfaces like ranges should be required to declare themselves as deliberately implementing said interface. Sure, we can tell people they should always `static assert(isForwardRage!MyType)`, but that's coding by convention and clearly isn't always going to happen.

Yeah, I find myself writing `static assert(isInputRange!MyType)` all the time these days, because you just never can be too sure you didn't screw up and cause things to mysteriously fail, even though they shouldn't.

Although I used to be a supporter of free-form sig constraints (and still am to some extent) and a hater of Concepts like in C++, more and more I'm beginning to realize the wisdom of Concepts rather than free-for-all ducktyping.  It's one of those things that work well in small programs and fast, one-shot projects, but don't generalize so well as you scale up to larger and larger projects.


T

-- 
A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. -- Michael B. Allen
September 15, 2018
On 9/13/18 3:53 PM, H. S. Teoh wrote:
> On Thu, Sep 13, 2018 at 06:32:54PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
>> On 09/11/2018 09:06 AM, Steven Schveighoffer wrote:
>>>
>>> Then I found the true culprit was isForwardRange!R. This led me to
>>> requestion my sanity, and finally realized I forgot the empty
>>> function.
>>
>> This is one reason template-based interfaces like ranges should be
>> required to declare themselves as deliberately implementing said
>> interface. Sure, we can tell people they should always `static
>> assert(isForwardRage!MyType)`, but that's coding by convention and
>> clearly isn't always going to happen.

No, please don't. I've used C# and Swift, and this sucks compared to duck typing.

> Yeah, I find myself writing `static assert(isInputRange!MyType)` all the
> time these days, because you just never can be too sure you didn't screw
> up and cause things to mysteriously fail, even though they shouldn't.
> 
> Although I used to be a supporter of free-form sig constraints (and
> still am to some extent) and a hater of Concepts like in C++, more and
> more I'm beginning to realize the wisdom of Concepts rather than
> free-for-all ducktyping.  It's one of those things that work well in
> small programs and fast, one-shot projects, but don't generalize so well
> as you scale up to larger and larger projects.

The problem I had was that it wasn't clear to me which constraint was failing. My bias brought me to "it must be autodecoding again!". But objectively, I should have examined all the constraints to see what was wrong. All C++ concepts seem to do (haven't used them) is help identify easier which requirements are failing.

We can fix all these problems by simply identifying the constraint clauses that fail. By color coding the error message identifying which ones are true and which are false, we can pinpoint the error without changing the language.

Once you fix the issue, it doesn't error any more, so the idea of duck typing and constraints is sound, it's just difficult to diagnose.

-Steve
September 15, 2018
On Saturday, 15 September 2018 at 15:31:00 UTC, Steven Schveighoffer wrote:
> The problem I had was that it wasn't clear to me which constraint was failing. My bias brought me to "it must be autodecoding again!". But objectively, I should have examined all the constraints to see what was wrong. All C++ concepts seem to do (haven't used them) is help identify easier which requirements are failing.

They also make it so your automated documentation can post a link to something that describes the type in more cases. std.algorithm would still be relatively horked, but a lot of functions could be declared as yielding, for instance, ForwardRange!(ElementType!(TRange)).

> We can fix all these problems by simply identifying the constraint clauses that fail. By color coding the error message identifying which ones are true and which are false, we can pinpoint the error without changing the language.

I wish. I had a look at std.algorithm.searching.canFind as the first thing I thought to check. Its constraints are of the form:

    bool canFind(Range)(Range haystack)
    if (is(typeof(find!pred(haystack))))

The compiler can helpfully point out that the specific constraint that failed was is(...), which does absolutely no good in trying to track down the problem.
September 15, 2018
On 9/15/18 12:04 PM, Neia Neutuladh wrote:
> On Saturday, 15 September 2018 at 15:31:00 UTC, Steven Schveighoffer wrote:
>> The problem I had was that it wasn't clear to me which constraint was failing. My bias brought me to "it must be autodecoding again!". But objectively, I should have examined all the constraints to see what was wrong. All C++ concepts seem to do (haven't used them) is help identify easier which requirements are failing.
> 
> They also make it so your automated documentation can post a link to something that describes the type in more cases. std.algorithm would still be relatively horked, but a lot of functions could be declared as yielding, for instance, ForwardRange!(ElementType!(TRange)).

True, we currently rely on convention there. But this really is simply documentation at a different (admittedly more verified) level.

> 
>> We can fix all these problems by simply identifying the constraint clauses that fail. By color coding the error message identifying which ones are true and which are false, we can pinpoint the error without changing the language.
> 
> I wish. I had a look at std.algorithm.searching.canFind as the first thing I thought to check. Its constraints are of the form:
> 
>      bool canFind(Range)(Range haystack)
>      if (is(typeof(find!pred(haystack))))
> 
> The compiler can helpfully point out that the specific constraint that failed was is(...), which does absolutely no good in trying to track down the problem.

is(typeof(...)) constraints might be useless here, but we have started to move away from such things in general (see for instance isInputRange and friends).

But there could actually be a solution -- just recursively play out the items at compile time (probably with the verbose switch) to see what underlying cause there is.

Other than that, you can then write find(myrange) and see what comes up.

In my case even, the problem was hasSlicing, which itself is a complicated template, and wouldn't have helped me diagnose the real problem. A recursive display of what things failed would help, but even if I could trigger a way to diagnose hasSlicing, instead of copying all the constraints locally, it's still a much better situation.

I'm really thinking of exploring how this could play out, just toying with the compiler to do this would give me experience in how the thing works.

-Steve