digitalmars.D - Iota
- React (31/31) Aug 04 2022 I saw Ali's presentation on 'iota'.
- Paul Backus (9/20) Aug 04 2022 Yeah, this is a classic case of what Andrei calls ["generality
- H. S. Teoh (30/54) Aug 04 2022 I'm probably partly to blame for this. :-/ Cf.:
- Paul Backus (14/19) Aug 04 2022 Strictly speaking, you don't even need that; all you need is a
- H. S. Teoh (11/33) Aug 04 2022 You don't even need a library prefab for that. Just do:
- Paul Backus (7/9) Aug 04 2022 Phobos v2, I guess? Leave the problematic overloads behind, and
- jmh530 (14/24) Aug 05 2022 There is probably a balance here between simple low level
- Salih Dincer (4/13) Aug 05 2022 I agree! It's like rangeTo and downTo in Kotlin, right? The
- Steven Schveighoffer (10/53) Aug 04 2022 Sorry, I'd rather specify the start, end, and steps, then have to work
- Steven Schveighoffer (4/17) Aug 05 2022 For some reason, the forum.dlang.org software regarded the paragraph
- Paul Backus (14/16) Aug 05 2022 Argument by analogy like this is exactly what leads to generality
- Steven Schveighoffer (12/21) Aug 05 2022 This isn't "argument by analogy". We have facilities built into the
- John Colvin (7/32) Aug 06 2022 I think that deserves to be a guiding principle that motivates
- Paul Backus (11/17) Aug 06 2022 One of the problems with `iota` is that, because it was built up
- H. S. Teoh (20/42) Aug 06 2022 Perhaps what we need is to sit down and come up with a consistent model
- Paul Backus (9/14) Aug 06 2022 I think there's a case to be made for allowing a separate
- H. S. Teoh (22/38) Aug 08 2022 Even the more reason to consider floating-point separately. As Walter
- Salih Dincer (39/41) Aug 10 2022 We did it 😀
- jmh530 (12/21) Aug 05 2022 This reminds me of Coase's "Nature of the Firm", which looks at
- Paul Backus (5/14) Aug 05 2022 Yes, absolutely. I'm not trying to suggest that we shouldn't have
- Walter Bright (14/14) Aug 06 2022 I haven't used iota much at all. But consider this. We all know exactly ...
- H. S. Teoh (7/30) Aug 06 2022 +1, iota with floating point was iffy to begin with. Let's take it off
- monkyyy (6/8) Aug 10 2022 Something being bad style shouldnt be grounds to break it
- monkyyy (17/22) Aug 04 2022 I disagree the issue is that the std is to general; I believe the
- Dom Disc (5/11) Aug 04 2022 Hmm. If for that type ++ does not what you (and iota) expect,
- Dom Disc (4/7) Aug 04 2022 IMHO, no floatingpoint type should not provide a ++, just like it
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
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
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: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.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
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
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: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.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.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
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
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: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.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 05 2022
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
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: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`. -SteveOn Thursday, 4 August 2022 at 16:28:14 UTC, React wrote: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)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
On 8/4/22 8:12 PM, Steven Schveighoffer wrote:On 8/4/22 3:06 PM, H. S. Teoh wrote:For some reason, the forum.dlang.org software regarded the paragraph below as belonging to Teoh. It was something I 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)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
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
On 8/5/22 11:20 AM, Paul Backus wrote:On Friday, 5 August 2022 at 00:12:40 UTC, Steven Schveighoffer wrote: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. -SteveAs 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.
Aug 05 2022
On Friday, 5 August 2022 at 15:31:18 UTC, Steven Schveighoffer wrote:On 8/5/22 11:20 AM, Paul Backus wrote: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.On Friday, 5 August 2022 at 00:12:40 UTC, Steven Schveighoffer wrote: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. -SteveAs 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.
Aug 06 2022
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
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: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?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
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
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: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.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 08 2022
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
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
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
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
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
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 isSomething 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
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
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
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