Thread overview
splitter string/char different behavior
Sep 30, 2017
SrMordred
Sep 30, 2017
Jon Degenhardt
Sep 30, 2017
WhatMeWorry
Sep 30, 2017
SrMordred
Sep 30, 2017
Jon Degenhardt
Sep 30, 2017
Jon Degenhardt
Sep 30, 2017
Jonathan M Davis
Oct 01, 2017
SrMordred
Oct 01, 2017
Jonathan M Davis
September 30, 2017
writeln( "a.b.c".splitter('.').dropBack(1) ); //compiles ok
writeln( "a.b.c".splitter(".").dropBack(1) );

//error:
Error: template std.range.dropBack cannot deduce function from argument types !()(Result, int), candidates are:
(...)

Hm.. can someone explain whats going on?

September 30, 2017
On Saturday, 30 September 2017 at 17:17:17 UTC, SrMordred wrote:
> writeln( "a.b.c".splitter('.').dropBack(1) ); //compiles ok
> writeln( "a.b.c".splitter(".").dropBack(1) );
>
> //error:
> Error: template std.range.dropBack cannot deduce function from argument types !()(Result, int), candidates are:
> (...)
>
> Hm.. can someone explain whats going on?

It's easy to overlook, but documentation for splitter starts out:

     Lazily splits a range using an element as a separator.

An element of a string is a char, not a string. It needs to be read somewhat literally, but it is correct.

It's also part of template constraint, useful once you've become accustomed to reading them:

    auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
        if (is(typeof(binaryFun!pred(r.front, s)) : bool) && ....

For "a.b.c"splitter(x), Range r is a string, r.front is a char. The template can only be instantiated if the predicate function is valid. The predicate function is "a == b". Since r.front is a char, then s must be a type that can be compared with '=='. A string and char cannot be compared with '==', which is why the a valid template instantiation could not be found.

September 30, 2017
On Saturday, 30 September 2017 at 18:21:11 UTC, Jon Degenhardt wrote:
> On Saturday, 30 September 2017 at 17:17:17 UTC, SrMordred wrote:
>> [...]
>
> It's easy to overlook, but documentation for splitter starts out:
>
>      Lazily splits a range using an element as a separator.
>
> An element of a string is a char, not a string. It needs to be read somewhat literally, but it is correct.
>
> It's also part of template constraint, useful once you've become accustomed to reading them:
>
>     auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
>         if (is(typeof(binaryFun!pred(r.front, s)) : bool) && ....
>
> For "a.b.c"splitter(x), Range r is a string, r.front is a char. The template can only be instantiated if the predicate function is valid. The predicate function is "a == b". Since r.front is a char, then s must be a type that can be compared with '=='. A string and char cannot be compared with '==', which is why the a valid template instantiation could not be found.

Would it be correct to just update the documentation to say "Lazily splits a range using an char as a separator" ?   what is it; wchar and dchar too?

I notice the example that is there has ' '  as the element.

September 30, 2017
>> For "a.b.c"splitter(x), Range r is a string, r.front is a char. The template can only be instantiated if the predicate function is valid. The predicate function is "a == b". Since r.front is a char, then s must be a type that can be compared with '=='. A string and char cannot be compared with '==', which is why the a valid template instantiation could not be found.
>
> Would it be correct to just update the documentation to say "Lazily splits a range using an char as a separator" ?   what is it; wchar and dchar too?
>
> I notice the example that is there has ' '  as the element.

But this works:
writeln("a.b.c".splitter(".") );
September 30, 2017
On Saturday, 30 September 2017 at 19:26:14 UTC, SrMordred wrote:
>>> For "a.b.c"splitter(x), Range r is a string, r.front is a char. The template can only be instantiated if the predicate function is valid. The predicate function is "a == b". Since r.front is a char, then s must be a type that can be compared with '=='. A string and char cannot be compared with '==', which is why the a valid template instantiation could not be found.
>>
>> Would it be correct to just update the documentation to say "Lazily splits a range using an char as a separator" ?   what is it; wchar and dchar too?
>>
>> I notice the example that is there has ' '  as the element.
>
> But this works:
> writeln("a.b.c".splitter(".") );

Geez, my mistake. I'm sorry about that. It's dropback that's failing, not splitter.
September 30, 2017
On Saturday, 30 September 2017 at 17:17:17 UTC, SrMordred wrote:
> writeln( "a.b.c".splitter('.').dropBack(1) ); //compiles ok
> writeln( "a.b.c".splitter(".").dropBack(1) );
>
> //error:
> Error: template std.range.dropBack cannot deduce function from argument types !()(Result, int), candidates are:
> (...)
>
> Hm.. can someone explain whats going on?

Let's try again. I'm not sure the full explanation, but likely involves two separate template overloads being instantiated, each with a separate definition of the return type.

* "a.b.c".splitter('.') - This overload: https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L3696-L3703

* "a.b.c".splitter(".") - This overload: https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L3973-L3982

But why one supports dropBack and the other doesn't I don't know.
September 30, 2017
On Saturday, September 30, 2017 20:18:25 Jon Degenhardt via Digitalmars-d- learn wrote:
> On Saturday, 30 September 2017 at 17:17:17 UTC, SrMordred wrote:
> > writeln( "a.b.c".splitter('.').dropBack(1) ); //compiles ok
> > writeln( "a.b.c".splitter(".").dropBack(1) );
> >
> > //error:
> > Error: template std.range.dropBack cannot deduce function from
> > argument types !()(Result, int), candidates are:
> > (...)
> >
> > Hm.. can someone explain whats going on?
>
> Let's try again. I'm not sure the full explanation, but likely involves two separate template overloads being instantiated, each with a separate definition of the return type.
>
> * "a.b.c".splitter('.') - This overload: https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L369 6-L3703
>
> * "a.b.c".splitter(".") - This overload: https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L397 3-L3982
>
> But why one supports dropBack and the other doesn't I don't know.

Well, figuring out where to split when iterating in reverse is trivial when splitting on a single element, but it's not when dealing with a range of elements. Sure, in this case, because the range happens to be only one character long, it would be easy, but as soon as it has multiple characters, it wouldn't be - especially if you got nonsense like

auto result = "ttttttttttttt".splitter("ttt");

In order to know where to split, it really has to do it from the front. If it starts from the back, you won't necessarily split in the same places as when iterating from the front, and that would violate how bidirectional ranges are supposed to work (the elements should be the same - just in reverse - if you iterate from the back). That being the case, it makes sense that splitting on a single element would result in a range that was bidirectional, whereas splitting on a range of elements would result in a range that's only a forward range.

- Jonathan M Davis

October 01, 2017
> In order to know where to split, it really has to do it from the front. If it starts from the back, you won't necessarily split in the same places as when iterating from the front, and that would violate how bidirectional ranges are supposed to work (the elements should be the same - just in reverse - if you iterate from the back). That being the case, it makes sense that splitting on a single element would result in a range that was bidirectional, whereas splitting on a range of elements would result in a range that's only a forward range.
>
> - Jonathan M Davis

Nice!
since dropBack is a BidirectionalRange everything make sense now.
Thanks everybody!

I just think that the error message should be a little better, since I have no idea about the incompatible Range types looking only to the error message. (Dont know if is possible, but anyway.. )
September 30, 2017
On Sunday, October 01, 2017 00:56:23 SrMordred via Digitalmars-d-learn wrote:
> > In order to know where to split, it really has to do it from the front. If it starts from the back, you won't necessarily split in the same places as when iterating from the front, and that would violate how bidirectional ranges are supposed to work (the elements should be the same - just in reverse - if you iterate from the back). That being the case, it makes sense that splitting on a single element would result in a range that was bidirectional, whereas splitting on a range of elements would result in a range that's only a forward range.
> >
> > - Jonathan M Davis
>
> Nice!
> since dropBack is a BidirectionalRange everything make sense now.
> Thanks everybody!
>
> I just think that the error message should be a little better, since I have no idea about the incompatible Range types looking only to the error message. (Dont know if is possible, but anyway.. )

When the compiler can't find a matching overload for a templated function, look at its template constraint, since you either passed the wrong number of arguments, the wrong type of arguments, or the arguments you passed failed the template constraint. The template constraints aren't always as easy to read as would be nice (especially if there are several overloads), but the key information is there.

- Jonathan M Davis