What is the 'Result' type even for?
H. S. Teoh
hsteoh at qfbox.info
Fri Jan 20 03:33:59 UTC 2023
On Fri, Jan 20, 2023 at 03:11:33AM +0000, Ruby The Roobster via Digitalmars-d-learn wrote:
> Take this example:
>
> ```d
> import std;
> void main()
> {
> auto c = "a|b|c|d|e".splitter('|');
> c.writeln;
> string[] e = ["a", "b", "c", "d", "e"];
> assert(c.equal(e));
> typeof(c).stringof.writeln;
> }
> ```
>
> The program prints:
>
> ["a", "b", "c", "d", "e"]
> Result
>
> What is the purpose of this 'Result' type? To serve as a generic
> range?
It's a Voldemort type, representing a range that iterates over its
elements lazily.
> Because, it seems to only cause problems. For example, you cannot
> assign or cast the result type into a range, even when the type has
> the same inherent function:
>
> ```d
> string[] c = "a|b|c|d|e".splitter('|'); // fails
> string[] d = cast(string[])"a|b|c|d|e".splitter('|'); // also fails
> ```
You're confusing arrays and ranges. A "range" isn't any specific type,
it refers to *any* type that behaves a certain way (behaves like a
range). Each `Result` you get back has its own unique type (arguably,
it's a compiler bug to display it as merely `Result` without
distinguishing it from other identically-named but distinct Voldemort
types), so you cannot just assign it back to an array.
You can either create an array from it using std.array.array, use a
function that eagerly creates its results instead of a lazy result (in
the above instance, use std.string.split instead of .splitter), or use
std.algorithm.copy to copy the contents of the lazy range into an array:
// Option 1
string[] c = "a|b|c|d|e".splitter('|').dup;
// Option 2
string[] c = "a|b|c|d|e".split('|');
// Option 3
// Caveat: .copy expects you to have prepared the buffer
// beforehand to be large enough to hold the contents; it does
// not reallocate the result array for you.
string[] result = new string[5];
"a|b|c|d|e".splitter('|').copy(result);
[...]
> Then what is the point of this type, if not to just make things
> difficult? It cannot be casted, and vector operations cannot be
> performed, and it seems to just serve as an unnecessary
> generalization.
It serves to chain further range operations into a pipeline:
string[] c = "a|b|c|d|e".splitter('|')
.filter!(c => c >= 'b' && c <= 'd')
.map!(c => c+1)
.array;
Because ranges are lazily iterated, the .array line only allocates the 3
elements that got through the .filter. Whereas if you created the
intermediate result array eagerly, you'd have to allocate space for 5
elements only to discard 2 of them afterwards.
One way to think about this is that the intermediate Result ranges are
like the middle part of a long pipe; you cannot get stuff from the
middle of the pipe without breaking it, you need to terminate the pipe
with a sink (like .array, .copy, etc.) first.
T
--
I am Pentium of Borg. Division is futile; you will be approximated.
More information about the Digitalmars-d-learn
mailing list