Re: What is the 'Result' type even for?

2023-01-20 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Jan 20, 2023 at 12:49:54PM +, Ruby The Roobster via 
Digitalmars-d-learn wrote:
[...]
> Thank you.  I didn't know that there was such a property `.array`.

It's not a property, it's a Phobos function from std.array.


T

-- 
INTEL = Only half of "intelligence".


Re: What is the 'Result' type even for?

2023-01-20 Thread Ruby The Roobster via Digitalmars-d-learn

On Friday, 20 January 2023 at 03:39:48 UTC, H. S. Teoh wrote:
On Fri, Jan 20, 2023 at 03:34:43AM +, Ruby The Roobster via 
Digitalmars-d-learn wrote:
On Friday, 20 January 2023 at 03:30:56 UTC, Steven 
Schveighoffer wrote:

> On 1/19/23 10:11 PM, Ruby The Roobster wrote:
> ...
> 
> The point is to be a range over the original input, 
> evaluated lazily. Using this building block, you can create 
> an array, or use some other algorithm, or whatever you want. 
> All without allocating more space to hold an array.

[...]
I get the point that it is supposed to be lazy.  But why are 
these basic cases not implemented?  I shouldn't have to go 
write a wrapper for something as simple as casting this type 
to the original type. This is one of the things that one 
expects the standard library to do for you.


There's no need to write any wrappers.  Just tack `.array` to 
the end of your pipeline, and you're good to go.



T


Thank you.  I didn't know that there was such a property `.array`.


Re: What is the 'Result' type even for?

2023-01-20 Thread Salih Dincer via Digitalmars-d-learn

On Friday, 20 January 2023 at 04:46:07 UTC, Ali Çehreli wrote:
Different instantiations of templates are distinct types. For 
example, if I called 'alternate' with two 'long' values, both 
alternate!int (as instantiated by the code above) and 
alternate!long would have different MyResult struct types.


In general, the ranges are compatible with each other because 
they use the empty, front, popFront interface. In the example 
below, different types (one of which is double) but the same 
ranges can be combined with chain(). However, it is necessary to 
convert it to array because of the opCmp() compatibility from 
algorithms such as sorting.


```d
  import std.algorithm : sort;
  import std.conv  : to;
  import std.range;
  import std.stdio;


  enum limit = 5;
  enum step = limit / 10.0;/*
  enum step = 1; //*/

void main()
{
  TypeInfo rangeType;   

  auto a = iota(limit);
  auto b = iota(step, limit, step);

  /* <- toggle comment, please add -> /
  auto ab = chain(a, b);
  rangeType = typeid(ab);/*/
  auto arrA = a.array.to!(double[]);
  auto arrB = b.array;
  auto ab = chain(arrA, arrB);
  rangeType = typeid(ab.sort);//*/

  ab.writeln(": ", rangeType);
} /*
current print:
==
[0, 0.5, 1, 1, 1.5, 2, 2, 2.5, 3, 3, 3.5, 4, 4, 4.5]: 
std.range.SortedRange!(std.range.chain!(double[], 
double[]).chain(double[], double[]).Result, "a < b", 
0).SortedRange


other print:

[0, 1, 2, 3, 4, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5]: 
std.range.chain!(std.range.iota!(int, int).iota(int, int).Result, 
std.range.iota!(double, int, double).iota(double, int, 
double).Result).chain(std.range.iota!(int, int).iota(int, 
int).Result, std.range.iota!(double, int, double).iota(double, 
int, double).Result).Result

```
SDB@79


Re: What is the 'Result' type even for?

2023-01-19 Thread Ali Çehreli via Digitalmars-d-learn

On 1/19/23 19:11, Ruby The Roobster wrote:

>  typeof(c).stringof.writeln;

> The program prints:
>
> ["a", "b", "c", "d", "e"]
> Result
>
> What is the purpose of this 'Result' type?

Just to make sure, 'Result' is what the programmer of a Phobos algorithm 
chose to name a struct type. It could be anything.


I will try to demonstrate it by naming my struct 'MyResult' below. The 
following range algorithm alternates between the two values it is called 
with.


The pragma(msg) inside 'main' prints MyResult.

auto alternate(T)(T a, T b) {
// This function will return an object
// of the following nested struct.
// (Note: This is for demonsration
// purposes only. Yes, this can be
// be more optimal.)
struct MyResult {
bool useB = false;

enum empty = false; // Infinite

T front() {
return useB ? b : a;
}

void popFront() {
// Flip the selector
useB = !useB;
}
}

// Here, an object of the struct is
// returned. It has single member (useB)
// that it uses as a selector.
// The values 'a' and 'b' are the actual
// function arguments.
return MyResult();
}

import std;

void main() {
auto myRange = alternate(42, 7);

// This prints 'MyResult' at compile time
pragma(msg, typeof(myRange));

const expected = [ 42, 7, 42, 7, 42 ];
assert(myRange.take(5).equal(expected));
}

> even when the type
> has the same inherent function:

Different instantiations of templates are distinct types. For example, 
if I called 'alternate' with two 'long' values, both alternate!int (as 
instantiated by the code above) and alternate!long would have different 
MyResult struct types.


Although they would have the same functionality, they would be compiled 
potentially with very different CPU instructions and would not be 
assignable.


Ali



Re: What is the 'Result' type even for?

2023-01-19 Thread Salih Dincer via Digitalmars-d-learn
On Friday, 20 January 2023 at 03:11:33 UTC, Ruby The Roobster 
wrote:


What is the purpose of this 'Result' type?  To serve as a 
generic range?  Because, it seems to only cause problems...


No.

When I first started learning this language, I thought the same 
thing.  However, such a designm consisting of structs was 
preferred because it is necessary for the ranges to be used as a 
whole (connected end-to-Znd like a pipe). qWalter has a very old 
[article](https://www.drdobbs.com/cpp/voldemort-types-in-d/232901591) about Voldemort which is the basis of D, pllease read it. D's ranges are very strong for those who know how to use it.


SDB@79


Re: What is the 'Result' type even for?

2023-01-19 Thread Basile B. via Digitalmars-d-learn
On Friday, 20 January 2023 at 03:11:33 UTC, Ruby The Roobster 
wrote:

Take this example:
[...]
What is the purpose of this 'Result' type?  To serve as a 
generic range?


Yes this is a lazy input range. Use `.array` to yield directly as 
a concrete value,

then you can append using `~=`.

Note that there are special functions to keep the laziness, e.g 
`chain` to happen an input range to another element-compatible 
input range


```
import std;
void main()
{
auto c = "a|b|c|d|e".splitter("|").chain(["f"]);
string[] e = ["a", "b", "c", "d", "e", "f"];
assert(c.equal(e));
}
```


Re: What is the 'Result' type even for?

2023-01-19 Thread Steven Schveighoffer via Digitalmars-d-learn

On 1/19/23 10:34 PM, Ruby The Roobster wrote:

On Friday, 20 January 2023 at 03:30:56 UTC, Steven Schveighoffer wrote:

On 1/19/23 10:11 PM, Ruby The Roobster wrote:
...

The point is to be a range over the original input, evaluated lazily. 
Using this building block, you can create an array, or use some other 
algorithm, or whatever you want. All without allocating more space to 
hold an array.




I get the point that it is supposed to be lazy.  But why are these basic 
cases not implemented?  I shouldn't have to go write a wrapper for 
something as simple as casting this type to the original type.  This is 
one of the things that one expects the standard library to do for you.


A range simply does not provide the API you are seeking. It provides 3 
methods:


front
popFront
empty

That's it. It does not provide appending. If you want appending or 
random access, use an array:


```d
auto c = "a|b|c|d|e".split('|');
static assert(is(typeof(c) == string[]));
// or:
auto c2 = "a|b|c|d|e".splitter('|').array; // convert range to an array
```

-Steve


Re: What is the 'Result' type even for?

2023-01-19 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Jan 20, 2023 at 03:34:43AM +, Ruby The Roobster via 
Digitalmars-d-learn wrote:
> On Friday, 20 January 2023 at 03:30:56 UTC, Steven Schveighoffer wrote:
> > On 1/19/23 10:11 PM, Ruby The Roobster wrote:
> > ...
> > 
> > The point is to be a range over the original input, evaluated
> > lazily. Using this building block, you can create an array, or use
> > some other algorithm, or whatever you want. All without allocating
> > more space to hold an array.
[...]
> I get the point that it is supposed to be lazy.  But why are these
> basic cases not implemented?  I shouldn't have to go write a wrapper
> for something as simple as casting this type to the original type.
> This is one of the things that one expects the standard library to do
> for you.

There's no need to write any wrappers.  Just tack `.array` to the end of
your pipeline, and you're good to go.


T

-- 
My father told me I wasn't at all afraid of hard work. I could lie down right 
next to it and go to sleep. -- Walter Bright


Re: What is the 'Result' type even for?

2023-01-19 Thread Ruby The Roobster via Digitalmars-d-learn
On Friday, 20 January 2023 at 03:30:56 UTC, Steven Schveighoffer 
wrote:

On 1/19/23 10:11 PM, Ruby The Roobster wrote:
...

The point is to be a range over the original input, evaluated 
lazily. Using this building block, you can create an array, or 
use some other algorithm, or whatever you want. All without 
allocating more space to hold an array.


-Steve


I get the point that it is supposed to be lazy.  But why are 
these basic cases not implemented?  I shouldn't have to go write 
a wrapper for something as simple as casting this type to the 
original type.  This is one of the things that one expects the 
standard library to do for you.


Re: What is the 'Result' type even for?

2023-01-19 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Jan 20, 2023 at 03:11:33AM +, 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.


Re: What is the 'Result' type even for?

2023-01-19 Thread Steven Schveighoffer via Digitalmars-d-learn

On 1/19/23 10:11 PM, Ruby The Roobster 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?  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
```


The `Result` type is an internal type that provides a lazily-evaluated 
range of the original string. That is, no array is allocated, and the 
actual searching of the `|` character isn't done until you start 
fetching each element.


It returns windows into the *original* string, and builds each window 
one element at a time (via slicing).


So when you say "cast to a range", you are incorrectly stating the type 
`string[]` as a "range", when you are trying to cast to an array of 
strings. The `Result` type *is* a range, it's a range of strings. But it 
is not an array. If you want an array, you can use the `split` function, 
which allocates an array to hold all the slices.



And if you need to perform a set operation?

```d
c[] ~= "lolno"; // fails, as [] isn't defined for Result.
```


Right, because a `Result` is not an array. Appending is an array 
operation, not a range operation.


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.


The point is to be a range over the original input, evaluated lazily. 
Using this building block, you can create an array, or use some other 
algorithm, or whatever you want. All without allocating more space to 
hold an array.


-Steve


Re: What is the 'Result' type even for?

2023-01-19 Thread Ruby The Roobster via Digitalmars-d-learn
On Friday, 20 January 2023 at 03:11:33 UTC, Ruby The Roobster 
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?  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

```

And if you need to perform a set operation?

```d
c[] ~= "lolno"; // fails, as [] isn't defined for 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.


Furthermore, it can also be confirmed that each member of c is a 
string, further solidifying my opinion of 'Result' as just being 
a generic range template, that cannot be casted to an array of 
the original type.