Re: Strange behavior of iota

2022-02-16 Thread jmh530 via Digitalmars-d-learn

On Wednesday, 16 February 2022 at 19:35:00 UTC, jmh530 wrote:

[snip]

Step 1: In the integral overloads, use allSatisfy!(isSigned, B, 
E) || allSatisfy!(isUnsigned, T, U) for the current behavior
Step 2: When !(allSatisfy!(isSigned, B, E) || 
allSatisfy!(isUnsigned, T, U)), then convert to narrowest 
common type as I mentioned (long in your case).


This would preserve the current size when the types are both 
either signed or unsigned and then would expand it only when 
there are different signed-ness. This also makes the behavior 
change at compile-time instead of throwing at runtime.


T/U should be B/E


Re: Strange behavior of iota

2022-02-16 Thread jmh530 via Digitalmars-d-learn

On Wednesday, 16 February 2022 at 15:55:55 UTC, bachmeier wrote:

On Wednesday, 16 February 2022 at 15:21:11 UTC, jmh530 wrote:

On Tuesday, 15 February 2022 at 22:24:53 UTC, bachmeier wrote:

[snip]

After looking at the documentation and seeing 
CommonType!(int, uint) is uint, I have to say that iota's 
behavior doesn't make much sense.


What do you propose as an alternative? What about the 
narrowest type that fits both int and uint? That would be a 
long.


My preference (in order)

1. Change everything to long. That way it works as anyone other 
than the author of std.range.iota would expect.
2. Throw an error when casting from signed to unsigned. That 
would at least prevent wrong output. The current behavior 
delivers incorrect output 100% of the time, excluding the 
trivial case where the correct output has zero elements.

3. Require the step to be positive.
4. Remove iota from Phobos because it silently changes correct 
code to incorrect code that compiles and runs.


I've got an idea for combining 1 and 2.

Step 1: In the integral overloads, use allSatisfy!(isSigned, B, 
E) || allSatisfy!(isUnsigned, T, U) for the current behavior
Step 2: When !(allSatisfy!(isSigned, B, E) || 
allSatisfy!(isUnsigned, T, U)), then convert to narrowest common 
type as I mentioned (long in your case).


This would preserve the current size when the types are both 
either signed or unsigned and then would expand it only when 
there are different signed-ness. This also makes the behavior 
change at compile-time instead of throwing at runtime.


Re: Strange behavior of iota

2022-02-16 Thread bachmeier via Digitalmars-d-learn

On Wednesday, 16 February 2022 at 15:21:11 UTC, jmh530 wrote:

On Tuesday, 15 February 2022 at 22:24:53 UTC, bachmeier wrote:

[snip]

After looking at the documentation and seeing CommonType!(int, 
uint) is uint, I have to say that iota's behavior doesn't make 
much sense.


What do you propose as an alternative? What about the narrowest 
type that fits both int and uint? That would be a long.


My preference (in order)

1. Change everything to long. That way it works as anyone other 
than the author of std.range.iota would expect.
2. Throw an error when casting from signed to unsigned. That 
would at least prevent wrong output. The current behavior 
delivers incorrect output 100% of the time, excluding the trivial 
case where the correct output has zero elements.

3. Require the step to be positive.
4. Remove iota from Phobos because it silently changes correct 
code to incorrect code that compiles and runs.


Re: Strange behavior of iota

2022-02-16 Thread jmh530 via Digitalmars-d-learn

On Tuesday, 15 February 2022 at 22:24:53 UTC, bachmeier wrote:

[snip]

After looking at the documentation and seeing CommonType!(int, 
uint) is uint, I have to say that iota's behavior doesn't make 
much sense.


What do you propose as an alternative? What about the narrowest 
type that fits both int and uint? That would be a long.


Re: Strange behavior of iota

2022-02-16 Thread bachmeier via Digitalmars-d-learn
On Wednesday, 16 February 2022 at 02:51:32 UTC, Era Scarecrow 
wrote:

On Tuesday, 15 February 2022 at 22:24:53 UTC, bachmeier wrote:
On Tuesday, 15 February 2022 at 22:02:13 UTC, Adam D Ruppe 
wrote:

for(a = v.length; a > cast(size_t) -1, a += -1)


After looking at the documentation and seeing CommonType!(int, 
uint) is uint, I have to say that iota's behavior doesn't make 
much sense.


Unless it's almost always intended to go up and stay positive?


That may have been the intended use case, but it's poor design to 
not enforce it. I was using it to iterate through the indexes on 
an array backwards. You have to set the second argument to -1 to 
get the last index to be 0.


On a larger scale, it's D's principle that it's okay to throw 
unsigned types around and leave it to the programmer to hang 
themself. It's hard to see how anyone could think it makes sense 
to silently convert -1 to 18446744073709551615 without even a 
warning.


This compiles:

```
void main() {
void foo(int x) {}
void bar(uint x) {}

bar(-1.to!uint);
}
```

This doesn't:

```
void main() {
void foo(int x) {}
void bar(uint x) {}

foo(-1.to!long);
}
```


Re: Strange behavior of iota

2022-02-15 Thread Era Scarecrow via Digitalmars-d-learn

On Tuesday, 15 February 2022 at 22:24:53 UTC, bachmeier wrote:
On Tuesday, 15 February 2022 at 22:02:13 UTC, Adam D Ruppe 
wrote:

for(a = v.length; a > cast(size_t) -1, a += -1)


After looking at the documentation and seeing CommonType!(int, 
uint) is uint, I have to say that iota's behavior doesn't make 
much sense.


Unless it's almost always intended to go up and stay positive?

Not that it can't be modified to take all those cases in, hitting 
64bit there's little reason you can't just use long,long for the 
arguments.


Re: Strange behavior of iota

2022-02-15 Thread bachmeier via Digitalmars-d-learn

On Tuesday, 15 February 2022 at 22:02:13 UTC, Adam D Ruppe wrote:

On Tuesday, 15 February 2022 at 21:48:29 UTC, bachmeier wrote:

writeln(iota(v.length,-1,-1));


This would be like

for(a = v.length; a > cast(size_t) -1, a += -1)


That (cast(size_t) -1) is the same as thing.max, meaning a will 
never be greater than it.


Why does the first argument to iota have to be an int, and why 
isn't there an error message when I pass something else?


Yeah, perhaps when you give it that -1 as the second argument 
it should see it is signed and throw something.


After looking at the documentation and seeing CommonType!(int, 
uint) is uint, I have to say that iota's behavior doesn't make 
much sense.


Re: Strange behavior of iota

2022-02-15 Thread Adam D Ruppe via Digitalmars-d-learn

On Tuesday, 15 February 2022 at 21:48:29 UTC, bachmeier wrote:

writeln(iota(v.length,-1,-1));


This would be like

for(a = v.length; a > cast(size_t) -1, a += -1)


That (cast(size_t) -1) is the same as thing.max, meaning a will 
never be greater than it.


Why does the first argument to iota have to be an int, and why 
isn't there an error message when I pass something else?


Yeah, perhaps when you give it that -1 as the second argument it 
should see it is signed and throw something.


Strange behavior of iota

2022-02-15 Thread bachmeier via Digitalmars-d-learn

This code

```
import std.conv, std.range, std.stdio;

void main() {
auto v = [1, 2, 3, 4, 5];
writeln(iota(v.length,-1,-1));
writeln(iota(v.length,-1.to!long,-1));
writeln(iota(v.length.to!int,-1,-1));
writeln(iota(v.length.to!uint,-1,-1));
writeln(iota(v.length.to!ulong,-1,-1));
}
```

outputs

```
[]
[]
[5, 4, 3, 2, 1, 0]
[]
[]
```

Why does the first argument to iota have to be an int, and why 
isn't there an error message when I pass something else?