www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - splitter string/char different behavior

reply SrMordred <patric.dexheimer gmail.com> writes:
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?
Sep 30
next sibling parent reply Jon Degenhardt <jond noreply.com> writes:
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.
Sep 30
parent reply WhatMeWorry <kheaser gmail.com> writes:
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.
Sep 30
parent reply SrMordred <patric.dexheimer gmail.com> writes:
 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(".") );
Sep 30
parent Jon Degenhardt <jond noreply.com> writes:
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.
Sep 30
prev sibling parent reply Jon Degenhardt <jond noreply.com> writes:
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.
Sep 30
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Sep 30
parent reply SrMordred <patric.dexheimer gmail.com> writes:
 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.. )
Sep 30
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Sep 30