www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Iota

reply React <react react.js> writes:
I saw Ali's presentation on 'iota'.

I don't want to start another thread about why D has some issues, 
despite the ammunition provided by the simple fact that you need 
37 slides to explain a simple function. If you ask me, it is 
enough to provide iota for int and maybe long, the rest can be 
obtained using map & co if you insist. Maybe I want dates. Or 
fluffy bears.

What I want to highlight, it's the fact that iota seems flawed: 
it assumes that every type supporting increment operators will 
generate a nice, consecutive, ordered sequence of values.

Iota specialises on floating point values exactly because of the 
problem above, but assumes that *any* other type will nicely 
behave as long you can put a ++ near a value.

We can ignore the hypotetical situation in which someone will 
find an unusual usage of ++ (probably there is one person that 
never imagined that << can be used for console output), but we 
already have a candidate where iota will fail: CustomFloat from 
std.numeric.

```dlang
import std;
void main()
{
     CustomFloat!16 cb = 0.98;
     CustomFloat!16 ce = 1.98099;
     auto x = iota(cb, ce);
     foreach (y; x)
         writeln(y);
     //outputs 0.97998, 1.98047
     //outputs 0.97998 only if ce is 1.98098
}
```
Aug 04 2022
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
 What I want to highlight, it's the fact that iota seems flawed: 
 it assumes that every type supporting increment operators will 
 generate a nice, consecutive, ordered sequence of values.

 Iota specialises on floating point values exactly because of 
 the problem above, but assumes that *any* other type will 
 nicely behave as long you can put a ++ near a value.

 We can ignore the hypotetical situation in which someone will 
 find an unusual usage of ++ (probably there is one person that 
 never imagined that << can be used for console output), but we 
 already have a candidate where iota will fail: CustomFloat from 
 std.numeric.
Yeah, this is a classic case of what Andrei calls ["generality creep"][1]. You start out with the obviously-correct core functionality. Then you start gradually extending it to handle new edge cases. Each one seems reasonable enough on its own, but by the end of the process, you find yourself with an API that's complex and hard to use, wondering where it all went wrong. [1]: https://forum.dlang.org/thread/q6plhj$1l9$1 digitalmars.com
Aug 04 2022
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, Aug 04, 2022 at 05:52:46PM +0000, Paul Backus via Digitalmars-d wrote:
 On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
 What I want to highlight, it's the fact that iota seems flawed: it
 assumes that every type supporting increment operators will generate
 a nice, consecutive, ordered sequence of values.
 
 Iota specialises on floating point values exactly because of the
 problem above, but assumes that *any* other type will nicely behave
 as long you can put a ++ near a value.
 
 We can ignore the hypotetical situation in which someone will find
 an unusual usage of ++ (probably there is one person that never
 imagined that << can be used for console output), but we already
 have a candidate where iota will fail: CustomFloat from std.numeric.
Yeah, this is a classic case of what Andrei calls ["generality creep"][1]. You start out with the obviously-correct core functionality. Then you start gradually extending it to handle new edge cases. Each one seems reasonable enough on its own, but by the end of the process, you find yourself with an API that's complex and hard to use, wondering where it all went wrong. [1]: https://forum.dlang.org/thread/q6plhj$1l9$1 digitalmars.com
I'm probably partly to blame for this. :-/ Cf.: https://issues.dlang.org/show_bug.cgi?id=6447 After thinking about this, I think in its barest essentials, the only version of iota we need is the one that takes a single int argument, and that generates that many numbers, starting from 0. Everything else can be built on top of this with other existing library functions. For example, if you want a range that starts from 10 and ends at 20, just write: iota(10).map!(i => i + 10) If you want a range that starts at 3 and steps by 5 each time, just write: iota(n).map!(i => 3 + i*5) If you want a floating-point range, just write: iota(n).map!(i => cast(double) i) If you want a range of values of a custom type that generates values via repeated applications of ++, just write: MyType startValue = ...; auto r = generate!({ return ++startValue; }).take(n); // Or, if you want to stop at some sentinel value: auto r = generate!({ return ++startValue; }) .until!(e => e == MyType.sentinelValue); This could be used, for example, to generate a range over an enum. If your custom type supports + and you want to use that to provide random access, just do: auto r = iota(n).map!(i => MyType.startValue + i); And so forth. Everything else is just icing, and not strictly necessary. T -- Give a man a fish, and he eats once. Teach a man to fish, and he will sit forever.
Aug 04 2022
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 4 August 2022 at 19:06:43 UTC, H. S. Teoh wrote:
 After thinking about this, I think in its barest essentials, 
 the only version of iota we need is the one that takes a single 
 int argument, and that generates that many numbers, starting 
 from 0.  Everything else can be built on top of this with other 
 existing library functions.
Strictly speaking, you don't even need that; all you need is a range that generates the natural numbers, and you can write naturals.take(n) ...to get the equivalent of iota(n). That said, I don't think you have to aim for total minimalism to avoid generality creep here. You just need to know what iota's purpose for inclusion is, so that you can say "no, that's not iota's job; use generate/recurrence/something else instead" when someone proposes an overly-general enhancement. I wonder if perhaps iota was vulnerable to generality creep in part because it was copied into Phobos from C++'s STL, without a lot of thought given to what specific purpose it served in relation to the rest of the library.
Aug 04 2022
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, Aug 04, 2022 at 09:31:02PM +0000, Paul Backus via Digitalmars-d wrote:
 On Thursday, 4 August 2022 at 19:06:43 UTC, H. S. Teoh wrote:
 After thinking about this, I think in its barest essentials, the
 only version of iota we need is the one that takes a single int
 argument, and that generates that many numbers, starting from 0.
 Everything else can be built on top of this with other existing
 library functions.
Strictly speaking, you don't even need that; all you need is a range that generates the natural numbers, and you can write naturals.take(n) ...to get the equivalent of iota(n).
You don't even need a library prefab for that. Just do: auto r = recurrence!"a[n-1] + 1"(0); // equivalent to `naturals` You can then tack on .take(n) or whatever else to get what you want. But having to write this every time you need a number range is rather annoying. So there's *some* role here for iota to fill.
 That said, I don't think you have to aim for total minimalism to avoid
 generality creep here. You just need to know what iota's purpose for
 inclusion is, so that you can say "no, that's not iota's job; use
 generate/recurrence/something else instead" when someone proposes an
 overly-general enhancement.

 I wonder if perhaps iota was vulnerable to generality creep in part
 because it was copied into Phobos from C++'s STL, without a lot of
 thought given to what specific purpose it served in relation to the
 rest of the library.
Perhaps. But the question now is, where do we go from here? How do we improve what we currently have? T -- War doesn't prove who's right, just who's left. -- BSD Games' Fortune
Aug 04 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 4 August 2022 at 22:36:57 UTC, H. S. Teoh wrote:
 Perhaps.  But the question now is, where do we go from here?  
 How do we improve what we currently have?
Phobos v2, I guess? Leave the problematic overloads behind, and tell people upgrading to use a different function if they need something fancier. Unless there's been a big policy reversal announced at DConf, my impression is that breaking changes to existing Phobos interfaces are off the table.
Aug 04 2022
parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 5 August 2022 at 00:56:04 UTC, Paul Backus wrote:
 On Thursday, 4 August 2022 at 22:36:57 UTC, H. S. Teoh wrote:
 Perhaps.  But the question now is, where do we go from here?  
 How do we improve what we currently have?
Phobos v2, I guess? Leave the problematic overloads behind, and tell people upgrading to use a different function if they need something fancier. Unless there's been a big policy reversal announced at DConf, my impression is that breaking changes to existing Phobos interfaces are off the table.
There is probably a balance here between simple low level constructions but at the same time a convenient high level API. As a user, I would just much rather be able to do something like `x.iota(a, b)` where `x` could be anything that it might make sense to call `iota` on (I like "Design by Introspection" with a bit of duck typing thrown in). The issue with something like `iota(n).map!(i => MyType.startValue + i)` is that I might have to write that all the time as a user. From my perspective, I would rather put an `iota` overload in `MyType`'s module if I can't have it as part of `phobos`. From this perspective, `std.algorithm` should support built-in types and other types could define their own `iota` that call these.
Aug 05 2022
prev sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 4 August 2022 at 21:31:02 UTC, Paul Backus wrote:
 Strictly speaking, you don't even need that; all you need is a 
 range that generates the natural numbers, and you can write

     naturals.take(n)

 ...to get the equivalent of iota(n).

 That said, I don't think you have to aim for total minimalism 
 to avoid generality creep here. You just need to know what 
 iota's purpose for inclusion is, so that you can say "no, 
 that's not iota's job; use generate/recurrence/something else 
 instead" when someone proposes an overly-general enhancement.
I agree! It's like rangeTo and downTo in Kotlin, right? The simpler, the better! SDB 79
Aug 05 2022
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/4/22 3:06 PM, H. S. Teoh wrote:
 On Thu, Aug 04, 2022 at 05:52:46PM +0000, Paul Backus via Digitalmars-d wrote:
 On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
 What I want to highlight, it's the fact that iota seems flawed: it
 assumes that every type supporting increment operators will generate
 a nice, consecutive, ordered sequence of values.

 Iota specialises on floating point values exactly because of the
 problem above, but assumes that *any* other type will nicely behave
 as long you can put a ++ near a value.

 We can ignore the hypotetical situation in which someone will find
 an unusual usage of ++ (probably there is one person that never
 imagined that << can be used for console output), but we already
 have a candidate where iota will fail: CustomFloat from std.numeric.
Yeah, this is a classic case of what Andrei calls ["generality creep"][1]. You start out with the obviously-correct core functionality. Then you start gradually extending it to handle new edge cases. Each one seems reasonable enough on its own, but by the end of the process, you find yourself with an API that's complex and hard to use, wondering where it all went wrong. [1]: https://forum.dlang.org/thread/q6plhj$1l9$1 digitalmars.com
I'm probably partly to blame for this. :-/ Cf.: https://issues.dlang.org/show_bug.cgi?id=6447 After thinking about this, I think in its barest essentials, the only version of iota we need is the one that takes a single int argument, and that generates that many numbers, starting from 0. Everything else can be built on top of this with other existing library functions. For example, if you want a range that starts from 10 and ends at 20, just write: iota(10).map!(i => i + 10) If you want a range that starts at 3 and steps by 5 each time, just write: iota(n).map!(i => 3 + i*5)
Sorry, I'd rather specify the start, end, and steps, then have to work backwards from the formula what it means. Clarity is important. Also important is not making the compiler do extra calculations (or the optimizer) when it doesn't have to. A mapping might correctly express intent, but might also fool the optimizer from using a better path that ++ or += would use. As for the floating point thing, `foreach(i; 0.5 .. 7.6)` works, it should too for `iota`. -Steve
Aug 04 2022
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/4/22 8:12 PM, Steven Schveighoffer wrote:
 On 8/4/22 3:06 PM, H. S. Teoh wrote:
 For example, if you want a range that starts from 10 and ends at 20,
 just write:

     iota(10).map!(i => i + 10)

 If you want a range that starts at 3 and steps by 5 each time, just 
 write:

     iota(n).map!(i => 3 + i*5)
For some reason, the forum.dlang.org software regarded the paragraph below as belonging to Teoh. It was something I wrote.
 Sorry, I'd rather specify the start, end, and steps, then have to work 
 backwards from the formula what it means. Clarity is important.
 
-Steve
Aug 05 2022
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 5 August 2022 at 00:12:40 UTC, Steven Schveighoffer 
wrote:
 As for the floating point thing, `foreach(i; 0.5 .. 7.6)` 
 works, it should too for `iota`.
Argument by analogy like this is exactly what leads to generality creep in the first place. You can just as easily say, "`for (auto i = start; i < end; i++)` works for any type that overloads `<` and `++`, so `iota(start, end)` should do the same", and then you end up with the exact problem OP describes. There has to be some deeper principle that lets you decide which analogies are valid and which aren't. Like, what is our purpose for including `iota` in Phobos, when we already have more general facilities like `recurrence`, `sequence`, and `generate`; and does adding a particular feature to `iota` serve that purpose? In this case I think your conclusion is correct, but the reasoning behind it is iffy.
Aug 05 2022
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/5/22 11:20 AM, Paul Backus wrote:
 On Friday, 5 August 2022 at 00:12:40 UTC, Steven Schveighoffer wrote:
 As for the floating point thing, `foreach(i; 0.5 .. 7.6)` works, it 
 should too for `iota`.
Argument by analogy like this is exactly what leads to generality creep in the first place. You can just as easily say, "`for (auto i = start; i < end; i++)` works for any type that overloads `<` and `++`, so `iota(start, end)` should do the same", and then you end up with the exact problem OP describes.
This isn't "argument by analogy". We have facilities built into the language which support an interface of "x to y". If iota is going to be the range equivalent, it should at least support that for all types that are supported for built-in range expressions. A consistent API is important. As for working with arbitrary types, I don't see a problem with it. If you define ++ on your type or += 1, and it doesn't mean the same as every other type that defines that, then it's on you for not following the conventions. D is a language which uses the introspected abilities of things to define whether they are compatible. -Steve
Aug 05 2022
next sibling parent John Colvin <john.loughran.colvin gmail.com> writes:
On Friday, 5 August 2022 at 15:31:18 UTC, Steven Schveighoffer 
wrote:
 On 8/5/22 11:20 AM, Paul Backus wrote:
 On Friday, 5 August 2022 at 00:12:40 UTC, Steven Schveighoffer 
 wrote:
 As for the floating point thing, `foreach(i; 0.5 .. 7.6)` 
 works, it should too for `iota`.
Argument by analogy like this is exactly what leads to generality creep in the first place. You can just as easily say, "`for (auto i = start; i < end; i++)` works for any type that overloads `<` and `++`, so `iota(start, end)` should do the same", and then you end up with the exact problem OP describes.
This isn't "argument by analogy". We have facilities built into the language which support an interface of "x to y". If iota is going to be the range equivalent, it should at least support that for all types that are supported for built-in range expressions. A consistent API is important. As for working with arbitrary types, I don't see a problem with it. If you define ++ on your type or += 1, and it doesn't mean the same as every other type that defines that, then it's on you for not following the conventions. D is a language which uses the introspected abilities of things to define whether they are compatible. -Steve
I think that deserves to be a guiding principle that motivates the design rather than a hard constraint. Imo nothing particularly bad happens here if iota is slightly more limited. I don’t really have an overall opinion on the “what should iota take” topic though.
Aug 06 2022
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 5 August 2022 at 15:31:18 UTC, Steven Schveighoffer 
wrote:
 As for working with arbitrary types, I don't see a problem with 
 it. If you define ++ on your type or += 1, and it doesn't mean 
 the same as every other type that defines that, then it's on 
 you for not following the conventions. D is a language which 
 uses the introspected abilities of things to define whether 
 they are compatible.
One of the problems with `iota` is that, because it was built up by accretion of special cases, it does *not* adhere to any kind of consistent structural interface. For example, this works: iota(BigInt(1), BigInt(10)) ...but this does not: iota(BigInt(1), BigInt(10), BigInt(1)) ...because nobody ever bothered to add a `(start, end, step)` overload for custom types.
Aug 06 2022
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Aug 06, 2022 at 03:11:28PM +0000, Paul Backus via Digitalmars-d wrote:
 On Friday, 5 August 2022 at 15:31:18 UTC, Steven Schveighoffer wrote:
 As for working with arbitrary types, I don't see a problem with it.
 If you define ++ on your type or += 1, and it doesn't mean the same
 as every other type that defines that, then it's on you for not
 following the conventions. D is a language which uses the
 introspected abilities of things to define whether they are
 compatible.
One of the problems with `iota` is that, because it was built up by accretion of special cases, it does *not* adhere to any kind of consistent structural interface. For example, this works: iota(BigInt(1), BigInt(10)) ...but this does not: iota(BigInt(1), BigInt(10), BigInt(1)) ...because nobody ever bothered to add a `(start, end, step)` overload for custom types.
Perhaps what we need is to sit down and come up with a consistent model for iota and implement that. A consistent API of what is expected of incoming types and how iota will behave in each case. Here's a first stab at it. Let T be the incoming type (I'm leaning against separately parametrizing the start/end/increment types, I think that's just needlessly complex). Then: 0. T must at least support < and at least one of ++, +. If not, iota(T) is not supported. 1. If T supports ++, then iota(start,end) will return at least an input range. 2. If `T + int` is valid, then iota(start,end) will return at least a random-access range. 3. If `T + T` is valid, then iota(start,end,step) will return at least an input range. 4. If `T + int*T` is valid, then iota(start,end,step) will return a random-access range. T -- Leather is waterproof. Ever see a cow with an umbrella?
Aug 06 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 6 August 2022 at 15:52:13 UTC, H. S. Teoh wrote:
 Here's a first stab at it. Let T be the incoming type (I'm 
 leaning against separately parametrizing the 
 start/end/increment types, I think that's just needlessly 
 complex). Then:

 [...]
I think there's a case to be made for allowing a separate increment type, so that you can have e.g. pointers as endpoints with a ptrdiff_t increment. But it's not super essential. There's also the single-argument iota(n) case to consider. If we're going to go all-in on structural typing, we need a convention for obtaining the "zero" value of a generic type T. The obvious choice is T.init, but that won't work for floating-point types, so maybe `cast(T) 0` is better?
Aug 06 2022
parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Aug 06, 2022 at 05:40:54PM +0000, Paul Backus via Digitalmars-d wrote:
 On Saturday, 6 August 2022 at 15:52:13 UTC, H. S. Teoh wrote:
 Here's a first stab at it. Let T be the incoming type (I'm leaning
 against separately parametrizing the start/end/increment types, I
 think that's just needlessly complex). Then:
 
 [...]
I think there's a case to be made for allowing a separate increment type, so that you can have e.g. pointers as endpoints with a ptrdiff_t increment. But it's not super essential. There's also the single-argument iota(n) case to consider. If we're going to go all-in on structural typing, we need a convention for obtaining the "zero" value of a generic type T. The obvious choice is T.init, but that won't work for floating-point types, so maybe `cast(T) 0` is better?
Even the more reason to consider floating-point separately. As Walter said, the core should be integer-only. Floating-point support should be handled separately. I'm inclined to leave out floating-point entirely, actually. For example, if the user asks for iota(x,y) where x, y are floats and x and y are so large that x++ < nextUp(x), then how many elements should the resulting range have? If we say, execute ++ each time, then we may have an infinite range. But if we say use z = x + i*y, then it will be a finite range, but will generate the same floating-point values multiple times (because the difference between successive values is too small to be represented, so `x + i*y` will generate the same value for multiple values of i). Which behaviour is wanted by the user? No one but the user can say. Instead of trying to support all of these corner cases, we should just leave it to the user to use .generate with his formula of choice to produce a range with the desired properties, instead of overloading .iota with arbitrary decisions that add significant complexity to the code but may or may not be what the user even wants in the first place. T -- 2+2=4. 2*2=4. 2^2=4. Therefore, +, *, and ^ are the same operation.
Aug 08 2022
prev sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Saturday, 6 August 2022 at 15:11:28 UTC, Paul Backus wrote:
 ...because nobody ever bothered to add a `(start, end, step)` 
 overload for custom types.
We did it 😀 ```d import //sdb.container, std.bigint, std.stdio; void main() { auto f = BigInt(ulong.max); auto s = BigInt(10); auto l = f + 10; //iota(f, l, s).writeln; // compile error iras(f, l, s).writeln; // no problem :) // [18446744073709551615, 18446744073709551625] } alias ir = inclusiveRange; alias iras = inclusiveRange; void inclusiveRange(T)(T Args...) if(is(T == bool)) { static assert(0, "\nBoolean type cannot be used!"); } auto inclusiveRange(T = int) (T f = T(0), T l = T(0), T s = T(1)) if(!is(T == bool)) { if(!l) { l = f; f = 0; } return InclusiveRange!T(f, l, s); } /* Error: * operation not allowed on `bool` `this.last -= this.step` */ struct InclusiveRange(T) { T front, last, step, term; //... ``` The continuation of the code: https://forum.dlang.org/post/nffqokazngtknxetiinu forum.dlang.org SDB 79
Aug 10 2022
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 5 August 2022 at 15:20:12 UTC, Paul Backus wrote:
 [snip]

 There has to be some deeper principle that lets you decide 
 which analogies are valid and which aren't. Like, what is our 
 purpose for including `iota` in Phobos, when we already have 
 more general facilities like `recurrence`, `sequence`, and 
 `generate`; and does adding a particular feature to `iota` 
 serve that purpose?

 In this case I think your conclusion is correct, but the 
 reasoning behind it is iffy.
This reminds me of Coase's "Nature of the Firm", which looks at the question of when firms should do things within the firm or contract them outside. If your language only has low level primitives that can build up everything, then there is an additional cost for users to figure out how those things combine. Higher level API (or more generality) can help reduce the cost that users face, but also have a trade-off by making the code base more difficult to maintain, etc. What you're suggesting puts all the cost on the user. I would argue that there is a trade-off, such that it makes sense in some places to offer higher level API for common use cases.
Aug 05 2022
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 5 August 2022 at 17:03:35 UTC, jmh530 wrote:
 If your language only has low level primitives that can build 
 up everything, then there is an additional cost for users to 
 figure out how those things combine. Higher level API (or more 
 generality) can help reduce the cost that users face, but also 
 have a trade-off by making the code base more difficult to 
 maintain, etc. What you're suggesting puts all the cost on the 
 user. I would argue that there is a trade-off, such that it 
 makes sense in some places to offer higher level API for common 
 use cases.
Yes, absolutely. I'm not trying to suggest that we shouldn't have high-level APIs. I'm trying to suggest that our high-level APIs should be the product of conscious design choices, rather than gradual accretion of special cases (a.k.a. "generality creep").
Aug 05 2022
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
I haven't used iota much at all. But consider this. We all know exactly what 
integer arithmetic does, as long as it doesn't overflow.

     for (i = start; i < end; i += step)

I've been around the block enough times with floating point arithmetic that I 
would *not* use iota with it. The reason is simple enough - if each step is an 
addition, there's an extra rounding error added on with each increment.

     for (d = start; d < end; d += step)

as opposed to:

     for (d = start; d < end; (d = start + step * i, ++i))

which doesn't have roundoff error accumulation, and might even be faster. How 
would I know which one iota uses?

I suggest seriously considering (for std2) having iota only work with integers. 
Floating point stuff can be done as a wrapper around iota.

But the core iota will be simple and understandable.
Aug 06 2022
next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Aug 06, 2022 at 09:35:08AM -0700, Walter Bright via Digitalmars-d wrote:
 I haven't used iota much at all. But consider this. We all know
 exactly what integer arithmetic does, as long as it doesn't overflow.
 
     for (i = start; i < end; i += step)
 
 I've been around the block enough times with floating point arithmetic
 that I would *not* use iota with it. The reason is simple enough - if
 each step is an addition, there's an extra rounding error added on
 with each increment.
 
     for (d = start; d < end; d += step)
 
 as opposed to:
 
     for (d = start; d < end; (d = start + step * i, ++i))
 
 which doesn't have roundoff error accumulation, and might even be
 faster.  How would I know which one iota uses?
 
 I suggest seriously considering (for std2) having iota only work with
 integers. Floating point stuff can be done as a wrapper around iota.
 
 But the core iota will be simple and understandable.
+1, iota with floating point was iffy to begin with. Let's take it off the table, start from a clean slate with integers only, and provide the facilities for grafting everything else on using separate facilities. T -- What do you call optometrist jokes? Vitreous humor.
Aug 06 2022
prev sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 6 August 2022 at 16:35:08 UTC, Walter Bright wrote:
 I've been around the block enough times with floating point 
 arithmetic that I would *not* use iota with it. The reason is
Something being bad style shouldnt be grounds to break it If iota was sanely implemented as `countby(T start, T end, S step)` and I used floats; some floating error should be a "wontfix" not something you engineer around and increase the complexity.
Aug 10 2022
prev sibling next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
 it assumes that every type supporting increment operators will 
 generate a nice, consecutive, ordered sequence of values.

 Iota specialises on floating point values exactly because of 
 the problem above, but assumes that *any* other type will 
 nicely behave as long you can put a ++ near a value.
I disagree the issue is that the std is to general; I believe the issue is that they dont stop adding details to this or that template and try to bandaid issues. If a 10 line function works in 90% of cases but is hard to extand to be correct 100% of the time; the simpler version would be perferable. But instead they use template constains and traits and who knows what else to try to narrow down edge cases for big chunks of the std. Iota could be split into a 10 line function called counter that returns int from a range and countby would return x while x<y; x+= z and would fit a float use case which would probaly be anouther 10 lines If the code was approachable you could allow more edge cases as its would be easier to debug; but theres gaint 1k line functions and most poeple including me treat reading the std as a last resort
Aug 04 2022
prev sibling parent reply Dom Disc <dominikus scherkl.de> writes:
On Thursday, 4 August 2022 at 16:28:14 UTC, React wrote:
 I saw Ali's presentation on 'iota'.
[...]
 We can ignore the hypotetical situation in which someone will 
 find an unusual usage of ++ (probably there is one person that 
 never imagined that << can be used for console output), but we 
 already have a candidate where iota will fail: CustomFloat from 
 std.numeric.
Hmm. If for that type ++ does not what you (and iota) expect, then I would ask if that is the fault of iota, or if this type would better NOT provide a ++ operator at all.
Aug 04 2022
parent Dom Disc <dominikus scherkl.de> writes:
On Thursday, 4 August 2022 at 19:06:12 UTC, Dom Disc wrote:
 Hmm. If for that type ++ does not what you (and iota) expect, 
 then I would ask if that is the fault of iota, or if this type 
 would better NOT provide a ++ operator at all.
IMHO, no floatingpoint type should not provide a ++, just like it doesn't provide % or >>. Instead iota with stepwidth should be used.
Aug 04 2022