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