padLeft appears to be implicitly converting the input string into an array of uints. The documentation mentions using byGrapheme, but the compiler can't handle it.
---------------------------------------------------
import std.range : padLeft;
import std.stdio : writeln;
import std.uni : byGrapheme;
import std.array : array;
void main()
{
string s = "a string";
writeln(s.padLeft('0', 10)); // [48, 48, 97, 32, 115, 116, 114, 105, 110, 103]
writeln("a string".padLeft('0', 10)); // [48, 48, 97, 32, 115, 116, 114, 105, 110, 103]
writeln("a string".padLeft(dchar('0'), 10)); // "00a string" (correct!)
// example directly from the documentation
writeln("abc".padLeft('_', 6)); // [95, 95, 95, 97, 98, 99]
// Error: template std.range.padLeft cannot deduce function from argument types !()(Result!string, char, int)
// writeln(byGrapheme("abc").padLeft('_', 6));
// Error: template std.range.padLeft cannot deduce function from argument types !()(Grapheme[], char, int),
// writeln(byGrapheme("abc").array.padLeft('_', 6));
}
Comment #1 by shove — 2019-06-19T05:25:30Z
I think the function you want is:
std.string.leftJustify(), std.string. rightJustify().
--------------------------------
std.range.padLeft() is normal, and it returns a range. The assert in the source file and the four lines I added are as follows:
--------------------------------
import std.algorithm.comparison : equal;
assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4]));
assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4]));
assert("abc".padLeft('_', 6).equal("___abc"));
assert("a string".padLeft('0', 10).equal("00a string"));
assert("a string".padLeft('0', 10).equal([48, 48, 97, 32, 115, 116, 114, 105, 110, 103]));
assert("a string".padLeft(dchar('0'), 10).equal("00a string"));
assert("a string".padLeft(dchar('0'), 10).equal([48, 48, 97, 32, 115, 116, 114, 105, 110, 103]));
Comment #2 by sgrammer — 2019-06-19T16:21:10Z
Thanks! I missed leftJustify. That is exactly what I need. If you are planning to add more asserts to document the behavior of padLeft, maybe consider adding:
-------------------------------------------------------
// implicitly converts to a range of uint when padding dchar[] with char
static assert(is(ElementType!(typeof("string".padLeft('0', 10))) == uint));
static assert(is(ElementType!(typeof("string".padLeft(dchar('0'), 10))) == dchar));
Comment #3 by shove — 2019-06-20T00:13:23Z
Thanks, I see. There's something unreasonable here.
Comment #4 by shove — 2019-06-20T02:32:35Z
The final step in the internal implementation of padLeft is chain() , and chain() converts multiple different types into a single type that can be accommodated via CommonType.
alias X = CommonType!(int, long, short);
assert(is(X == long));
alias Y = CommonType!(int, char[], short);
assert(is(Y == void));
--------------------------
static assert(is(ElementType!(typeof("string".padLeft('0', 10))) == uint));
static assert(is(ElementType!(typeof("string".padLeft(dchar('0'), 10))) == dchar));
The corresponding types of these two sentences are transformed as follows:
alias Z1 = CommonType!(char, dchar);
static assert(is(Z1 == uint));
alias Z2 = CommonType!(dchar, dchar);
static assert(is(Z2 == dchar));
So there is no problem with the design here, we can use it flexibly after mastering this feature.
Comment #5 by sgrammer — 2019-06-20T17:29:15Z
I didn't realize chain was so presumptuous. This makes me want to go check all of my code that uses chain and make sure I'm using it correctly. It IS documented though, which is comforting: "Due to safe type promotion in D, chaining together different character ranges results in a uint range."
Should we resolve this issue, or use it to improve documentation of some of these behaviors?