digitalmars.D - isInfinite isInadequate
- Peter Alexander (28/28) Oct 17 2012 So, std.range.isInfinite checks if r.empty is a compile time
- monarch_dodra (21/51) Oct 17 2012 Technically, while cycle does not assert, it will fail on the
- Peter Alexander (4/8) Oct 17 2012 cycle([]) is not infinite. A infinitely repeated empty range is
- monarch_dodra (6/14) Oct 17 2012 I'd argue that cycle should be redefined as "Repeats the
- deadalnix (3/3) Mar 12 2013 I want to resurrect that thread. Can someone explains the
- monarch_dodra (40/43) Mar 12 2013 The advantage of "enum empty = false" is that algorithms gain a
- Dmitry Olshansky (6/18) Mar 12 2013 I thought it goes in opposite direction - slicing of Infinite range
- Peter Alexander (3/14) Mar 12 2013 I believe the official stance on this is that self-assign is only
- monarch_dodra (43/54) Mar 12 2013 Its... complicated.
- Steven Schveighoffer (8/15) Mar 12 2013 Wouldn't it automatically be optimized out? I mean if r.empty is an enu...
- monarch_dodra (10/28) Mar 12 2013 Right, that's what I said. This first paragraph was just about
- Steven Schveighoffer (16/43) Mar 12 2013 Oh, ok. When you said "optmizing out" I thought you meant using
- deadalnix (8/16) Mar 12 2013 That will always be optimized away without trouble by existing
- deadalnix (14/57) Mar 12 2013 I think the point is moot for the same reason it is for
- Steven Schveighoffer (11/21) Mar 12 2013 Hm... is there a way to test for inifinitness without requiring to be an...
- Andrei Alexandrescu (5/27) Mar 12 2013 Crossed my mind a few times that fresh non-infinite ranges should be
- Steven Schveighoffer (17/51) Mar 12 2013 So something like this:
- monarch_dodra (9/56) Mar 12 2013 s/should/could/
- Steven Schveighoffer (12/54) Mar 12 2013 No, ranges can be initialized without a constructor. Structs are.
- monarch_dodra (10/32) Mar 12 2013 Depends on your definition of "initialized" I guess. Sure, you
- Timon Gehr (17/33) Mar 13 2013 std.algorithm.joiner assumes that default-initialized separator ranges
- Brad Roberts (6/9) Mar 12 2013 Where did this assertion come from? There's nothing about infinite that...
- monarch_dodra (2/16) Mar 12 2013 Yeah... ergo "can".
- Brad Roberts (4/21) Mar 12 2013 Ok. The use of 'can' there is generally a much stronger implication tha...
- Andrei Alexandrescu (3/4) Oct 17 2012 I think this is sensible.
- Walter Bright (5/24) Mar 12 2013 isInfinite tells you if it is statically determinable to be infinite, no...
So, std.range.isInfinite checks if r.empty is a compile time boolean equal to false. This works most of the time, but not always. Some ranges are infinite, but cannot be determined to be so at compile time (or even run time!) cycle([1, 2, 3]).until(4); // infinite, but not isInfinite cycle(new int[0]); // not infinite, but isInfinite collatzSequence(n).until(1); // infiniteness an open problem! In short, isInfinite does not -- and cannot -- work as a way of telling if a range is finite or infinite in general. On resolution is to take isInfinite at face value: it only tells you if a range is statically determined to be infinite. If isInfinite is false, it could still be infinite. This leaves us with the tricky cases like cycle(new int[0]). There's three resolutions to this (as far as I can tell): 1. Change cycle to not be an infinite range. 2. Make cycle assert when the source range is empty. 3. Ignore this issue. Option 1 is sound, but kind of sucks, because generally cycle is infinite. Option 2 is sound, but I don't like the idea of asserting on logically valid input just because the problem is too hard. Option 3 is not sound, but may be practical. Perhaps these edge-case, fake, infinite sequences are not worth worrying about -- just let it slide and make other people worry about the consequences. This Phobos pull request is relevant: https://github.com/D-Programming-Language/phobos/pull/871 Thoughts?
Oct 17 2012
On Wednesday, 17 October 2012 at 12:18:32 UTC, Peter Alexander wrote:So, std.range.isInfinite checks if r.empty is a compile time boolean equal to false. This works most of the time, but not always. Some ranges are infinite, but cannot be determined to be so at compile time (or even run time!) cycle([1, 2, 3]).until(4); // infinite, but not isInfinite cycle(new int[0]); // not infinite, but isInfinite collatzSequence(n).until(1); // infiniteness an open problem! In short, isInfinite does not -- and cannot -- work as a way of telling if a range is finite or infinite in general. On resolution is to take isInfinite at face value: it only tells you if a range is statically determined to be infinite. If isInfinite is false, it could still be infinite. This leaves us with the tricky cases like cycle(new int[0]). There's three resolutions to this (as far as I can tell): 1. Change cycle to not be an infinite range. 2. Make cycle assert when the source range is empty. 3. Ignore this issue. Option 1 is sound, but kind of sucks, because generally cycle is infinite. Option 2 is sound, but I don't like the idea of asserting on logically valid input just because the problem is too hard. Option 3 is not sound, but may be practical. Perhaps these edge-case, fake, infinite sequences are not worth worrying about -- just let it slide and make other people worry about the consequences. This Phobos pull request is relevant: https://github.com/D-Programming-Language/phobos/pull/871 Thoughts?cycle(new int[0]); // not infinite, but isInfinite 2. Make cycle assert when the source range is empty.Technically, while cycle does not assert, it will fail on the first call to front, either because of a divide by 0 (RA: index%length), or because of a call to an empty front. We should add an assert. IMO. -------- TBH, I do not see either as being a problem: Technically, cycle([]) *is* isInfinite, but the program will assert because of a run-time error due to the user's logic. Nobody said that just because a range is infinite, that it can't ever fail... Ranges that go on forever, but are not *isInfinite*. My stance on this point is that it is not a *big* problem either. I see it more as a runtime "infinite loop", rather than an compile time "infinite range". It's like a for loop where the run-time end condition will always be true. That said, if the user *does* know the range will be infinite, I wouldn't be against having an "assumeInfinite" template function, that can take any range, and transform into an (assumed) compile time infinite range.
Oct 17 2012
On Wednesday, 17 October 2012 at 12:33:54 UTC, monarch_dodra wrote:Technically, cycle([]) *is* isInfinite, but the program will assert because of a run-time error due to the user's logic. Nobody said that just because a range is infinite, that it can't ever fail...cycle([]) is not infinite. A infinitely repeated empty range is the empty range.
Oct 17 2012
On Wednesday, 17 October 2012 at 12:45:07 UTC, Peter Alexander wrote:On Wednesday, 17 October 2012 at 12:33:54 UTC, monarch_dodra wrote:I'd argue that cycle should be redefined as "Repeats the *content* of the given forward range ad infinitum." From there, passing it an empty range would be a logic error, since said range didn't have any content.Technically, cycle([]) *is* isInfinite, but the program will assert because of a run-time error due to the user's logic. Nobody said that just because a range is infinite, that it can't ever fail...cycle([]) is not infinite. A infinitely repeated empty range is the empty range.
Oct 17 2012
I want to resurrect that thread. Can someone explains the benefices of isInfinite ? I fail to see how it really benefit the code.
Mar 12 2013
On Tuesday, 12 March 2013 at 10:01:38 UTC, deadalnix wrote:I want to resurrect that thread. Can someone explains the benefices of isInfinite ? I fail to see how it really benefit the code.The advantage of "enum empty = false" is that algorithms gain a great performance boost by optimizing out any "if (r.empty)". This can be exploited for things like take, or anything that iterates as a matter of fact. I don't think anybody will argue that this is a bad approach. The trait itself checks if empty can be evaluated at compile time (to false). The advantage for the *coder* to know if the range is infinite is less obvious. One of the advantages is that an infinite range can have random access (meets RA requirements), even though it has no length member (normally, any RA range must have length). Having "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain. One of downsides to having infinite ranges is that their existence tends to make dealing with generic RA ranges a bit more difficult. A lot of our algorithms have changed requirements conditions from: "if (isRandomAccessRange!R)" to "if (isRandomAccessRange!R && hasLength!R)" or "if (isRandomAccessRange!R && !isInfinite!R)" //NOTE: Both are strictly equivalent: An RA range is either infinite, or has length, but not neither nor both. Up until not so long ago, it was not possible to slice infinite ranges. It is now possible. Unfortunatly, because RA ranges with slicing don't guarantee that r[0 .. $] is legal, things are still complicated in template code. The last point (IMO minor) that is raised in the thread is "runtime" infiniteness. EG: a range that may or may not be infinite at compile time, but which will be known at runtime. IMO, these a rare enough (arguably, inexistent) to not really matter. The workarounds are easy: 1) Make the range always infinite, but with a requirement that user must intialize it or whatnot. 2) Make a runtime fork that will call code that operates on a compile-time known infinite adaptor/subtype.
Mar 12 2013
12-Mar-2013 14:49, monarch_dodra пишет:Up until not so long ago, it was not possible to slice infinite ranges. It is now possible. Unfortunatly, because RA ranges with slicing don't guarantee that r[0 .. $] is legal, things are still complicated in template code.I thought it goes in opposite direction - slicing of Infinite range works only for ..$ slice. The chief new requirement of slicing is self-assign ability.The last point (IMO minor) that is raised in the thread is "runtime" infiniteness. EG: a range that may or may not be infinite at compile time, but which will be known at runtime. IMO, these a rare enough (arguably, inexistent) to not really matter. The workarounds are easy: 1) Make the range always infinite, but with a requirement that user must intialize it or whatnot. 2) Make a runtime fork that will call code that operates on a compile-time known infinite adaptor/subtype.-- Dmitry Olshansky
Mar 12 2013
On Tuesday, 12 March 2013 at 11:24:00 UTC, Dmitry Olshansky wrote:12-Mar-2013 14:49, monarch_dodra пишет:I believe the official stance on this is that self-assign is only required for non-infinite ranges.Up until not so long ago, it was not possible to slice infinite ranges. It is now possible. Unfortunatly, because RA ranges with slicing don't guarantee that r[0 .. $] is legal, things are still complicated in template code.I thought it goes in opposite direction - slicing of Infinite range works only for ..$ slice. The chief new requirement of slicing is self-assign ability.
Mar 12 2013
On Tuesday, 12 March 2013 at 11:24:00 UTC, Dmitry Olshansky wrote:12-Mar-2013 14:49, monarch_dodra пишет:Its... complicated. To be exact: hasSlicing only requires that: //-------- static if(isInfinite!R) typeof(take(r, 1)) s = r[1 .. 2]; else { static assert(is(typeof(r[1 .. 2]) == R)); R s = r[1 .. 2]; } //minor tests here //-------- Which means that if you are going to slice an RA, make sure it is finite before back-assigning. From there, *if* r[0 .. $] is legal, *then* extra checks are made: //-------- static if(is(typeof(r[0 .. $]))) { static assert(is(typeof(r[0 .. $]) == R)); //minor tests here } //-------- *Ideally*, for finite RA ranges, we would want to enforce that [0 .. $] MUST be legal. This would be breaking change however. We'd want the compiler to auto generate opDollar, which would make things simpler for everyone: http://d.puremagic.com/issues/show_bug.cgi?id=7177 The only reason this isn't done yet (AFAIK) is because: * Support for opDollar is still relativelly new. * exact details are not fully thought out yet. As for infinite ranges, ditto. Basically, once you've implemented "slice to end", normal slicing is also trivial. However, today, this is not the case, since it's breaking change. If you want to call "r[0 .. $]", you have to jump through hoops to know if it is legal code. ... Come to think about it, with today's "deprecated features are on by default", I think it would be possible to activate this new requirement without breaking any code. Those that activate -w would then notice their range needs change to meet the new hasSlicing requirements. traits + deprecation is funny business. jmdavis: What do you think?Up until not so long ago, it was not possible to slice infinite ranges. It is now possible. Unfortunatly, because RA ranges with slicing don't guarantee that r[0 .. $] is legal, things are still complicated in template code.I thought it goes in opposite direction - slicing of Infinite range works only for ..$ slice. The chief new requirement of slicing is self-assign ability.
Mar 12 2013
On Tue, 12 Mar 2013 06:49:56 -0400, monarch_dodra <monarchdodra gmail.com> wrote:On Tuesday, 12 March 2013 at 10:01:38 UTC, deadalnix wrote:Wouldn't it automatically be optimized out? I mean if r.empty is an enum, it's like saying if(false) I would think even with optimizations off, this might be done. Not that I'm questioning the value of isInfinite (I'm neutral on it), but this is not a benefit. -SteveI want to resurrect that thread. Can someone explains the benefices of isInfinite ? I fail to see how it really benefit the code.The advantage of "enum empty = false" is that algorithms gain a great performance boost by optimizing out any "if (r.empty)". This can be exploited for things like take, or anything that iterates as a matter of fact. I don't think anybody will argue that this is a bad approach.
Mar 12 2013
On Tuesday, 12 March 2013 at 14:30:00 UTC, Steven Schveighoffer wrote:On Tue, 12 Mar 2013 06:49:56 -0400, monarch_dodra <monarchdodra gmail.com> wrote:Right, that's what I said. This first paragraph was just about enum empty = false. The actual "isInfinite" discussion comes later. Another point: isInfinite is useful, if only to propagate infiniteness. For example: "1.repeat().map"a * 2"()". If "map" didn't know that repeat is infinite, it would simply provide the "dumb" empty implementation, and the final range will have lost it's infinite trait.On Tuesday, 12 March 2013 at 10:01:38 UTC, deadalnix wrote:Wouldn't it automatically be optimized out? I mean if r.empty is an enum, it's like saying if(false) I would think even with optimizations off, this might be done. Not that I'm questioning the value of isInfinite (I'm neutral on it), but this is not a benefit. -SteveI want to resurrect that thread. Can someone explains the benefices of isInfinite ? I fail to see how it really benefit the code.The advantage of "enum empty = false" is that algorithms gain a great performance boost by optimizing out any "if (r.empty)". This can be exploited for things like take, or anything that iterates as a matter of fact. I don't think anybody will argue that this is a bad approach.
Mar 12 2013
On Tue, 12 Mar 2013 11:02:43 -0400, monarch_dodra <monarchdodra gmail.com> wrote:On Tuesday, 12 March 2013 at 14:30:00 UTC, Steven Schveighoffer wrote:Oh, ok. When you said "optmizing out" I thought you meant using isInfinite to optionally remove calls to empty by hand.On Tue, 12 Mar 2013 06:49:56 -0400, monarch_dodra <monarchdodra gmail.com> wrote:Right, that's what I said. This first paragraph was just about enum empty = false.On Tuesday, 12 March 2013 at 10:01:38 UTC, deadalnix wrote:Wouldn't it automatically be optimized out? I mean if r.empty is an enum, it's like saying if(false) I would think even with optimizations off, this might be done. Not that I'm questioning the value of isInfinite (I'm neutral on it), but this is not a benefit. -SteveI want to resurrect that thread. Can someone explains the benefices of isInfinite ? I fail to see how it really benefit the code.The advantage of "enum empty = false" is that algorithms gain a great performance boost by optimizing out any "if (r.empty)". This can be exploited for things like take, or anything that iterates as a matter of fact. I don't think anybody will argue that this is a bad approach.Another point: isInfinite is useful, if only to propagate infiniteness. For example: "1.repeat().map"a * 2"()". If "map" didn't know that repeat is infinite, it would simply provide the "dumb" empty implementation, and the final range will have lost it's infinite trait.With inlining, this should propagate properly: struct WrapperRange(R) { R src; property auto front() { return src.front;} property bool empty() { return src.empty;} // should be optimized to 'false' with inlining void popFront() { src.popFront();} } If R's empty is an enum, then WrapperRange's empty should inline to the enum also. -Steve
Mar 12 2013
On Tuesday, 12 March 2013 at 15:02:44 UTC, monarch_dodra wrote:Right, that's what I said. This first paragraph was just about enum empty = false. The actual "isInfinite" discussion comes later. Another point: isInfinite is useful, if only to propagate infiniteness. For example: "1.repeat().map"a * 2"()". If "map" didn't know that repeat is infinite, it would simply provide the "dumb" empty implementation, and the final range will have lost it's infinite trait.That will always be optimized away without trouble by existing compilers. You'd loose the character of infinitness, but it seems to me like a lot of trouble as it add a whole class of things to consider when implementing wrapper ranges, for benefice that you can already get most of the time. Note that something like computeIfCTFEable!(r.empty, true) would be much more beneficial than the actual implementation.
Mar 12 2013
On Tuesday, 12 March 2013 at 10:49:57 UTC, monarch_dodra wrote:On Tuesday, 12 March 2013 at 10:01:38 UTC, deadalnix wrote:I think the point is moot for the same reason it is for SentinelRange. A function that return false will be inlined and the whole if optimized away anyway. Same goes for a constant. I'm not talking about a supposed sufficiently smart compiler, but actual compiler that exist right now.I want to resurrect that thread. Can someone explains the benefices of isInfinite ? I fail to see how it really benefit the code.The advantage of "enum empty = false" is that algorithms gain a great performance boost by optimizing out any "if (r.empty)". This can be exploited for things like take, or anything that iterates as a matter of fact. I don't think anybody will argue that this is a bad approach.The trait itself checks if empty can be evaluated at compile time (to false). The advantage for the *coder* to know if the range is infinite is less obvious. One of the advantages is that an infinite range can have random access (meets RA requirements), even though it has no length member (normally, any RA range must have length). Having "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain.Clearly this is a good point. I however think that a static assert within count is much better because it allow to give nicer feedback. The problem with InfiniteRange is that it does gives you cryptic error message like the count function do not exists.One of downsides to having infinite ranges is that their existence tends to make dealing with generic RA ranges a bit more difficult. A lot of our algorithms have changed requirements conditions from: "if (isRandomAccessRange!R)" to "if (isRandomAccessRange!R && hasLength!R)" or "if (isRandomAccessRange!R && !isInfinite!R)" //NOTE: Both are strictly equivalent: An RA range is either infinite, or has length, but not neither nor both. Up until not so long ago, it was not possible to slice infinite ranges. It is now possible. Unfortunatly, because RA ranges with slicing don't guarantee that r[0 .. $] is legal, things are still complicated in template code.I conclude that the fix introduced complexity without solving the problem, just pushing the limit. I think we should avoid this kind of stuff.The last point (IMO minor) that is raised in the thread is "runtime" infiniteness. EG: a range that may or may not be infinite at compile time, but which will be known at runtime. IMO, these a rare enough (arguably, inexistent) to not really matter. The workarounds are easy: 1) Make the range always infinite, but with a requirement that user must intialize it or whatnot. 2) Make a runtime fork that will call code that operates on a compile-time known infinite adaptor/subtype.A common case is unknown "infinitivenes".
Mar 12 2013
On Tue, 12 Mar 2013 11:25:54 -0400, deadalnix <deadalnix gmail.com> wrote:On Tuesday, 12 March 2013 at 10:49:57 UTC, monarch_dodra wrote:Hm... is there a way to test for inifinitness without requiring to be an enum? Wouldn't this also be valid? if(!R.init.empty) Essentially, you can evaluate R.init.empty at compile time AND it's false on an uninitialized range. How can a correctly written non-infinite range pass that? That would make forwarding much easier, as the 'dumb' implementation still would result in an infinite range. -SteveHaving "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain.Clearly this is a good point. I however think that a static assert within count is much better because it allow to give nicer feedback. The problem with InfiniteRange is that it does gives you cryptic error message like the count function do not exists.
Mar 12 2013
On 3/12/13 11:47 AM, Steven Schveighoffer wrote:On Tue, 12 Mar 2013 11:25:54 -0400, deadalnix <deadalnix gmail.com> wrote:Crossed my mind a few times that fresh non-infinite ranges should be empty. But there's no explicit requirement stating that, and I think e.g. one may define a k-elements buffer backed by in-situ storage. AndreiOn Tuesday, 12 March 2013 at 10:49:57 UTC, monarch_dodra wrote:Hm... is there a way to test for inifinitness without requiring to be an enum? Wouldn't this also be valid? if(!R.init.empty) Essentially, you can evaluate R.init.empty at compile time AND it's false on an uninitialized range. How can a correctly written non-infinite range pass that? That would make forwarding much easier, as the 'dumb' implementation still would result in an infinite range.Having "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain.Clearly this is a good point. I however think that a static assert within count is much better because it allow to give nicer feedback. The problem with InfiniteRange is that it does gives you cryptic error message like the count function do not exists.
Mar 12 2013
On Tue, 12 Mar 2013 12:16:10 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 3/12/13 11:47 AM, Steven Schveighoffer wrote:So something like this: struct R { int[4] elems; int idx; int front() { return elems[idx];} void popFront() {++idx;} bool empty() {idx < elems.length;} } which would make this an infinite range by my measurement. Crap. How can we make wrapping ranges easier? I get the point that it's a pain in the ass in order to implement a wrapper for another range for things like isInfinite, and save. We need something in std.range that allows you to wrap such functions, like a nice mixin. -SteveOn Tue, 12 Mar 2013 11:25:54 -0400, deadalnix <deadalnix gmail.com> wrote:Crossed my mind a few times that fresh non-infinite ranges should be empty. But there's no explicit requirement stating that, and I think e.g. one may define a k-elements buffer backed by in-situ storage.On Tuesday, 12 March 2013 at 10:49:57 UTC, monarch_dodra wrote:Hm... is there a way to test for inifinitness without requiring to be an enum? Wouldn't this also be valid? if(!R.init.empty) Essentially, you can evaluate R.init.empty at compile time AND it's false on an uninitialized range. How can a correctly written non-infinite range pass that? That would make forwarding much easier, as the 'dumb' implementation still would result in an infinite range.Having "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain.Clearly this is a good point. I however think that a static assert within count is much better because it allow to give nicer feedback. The problem with InfiniteRange is that it does gives you cryptic error message like the count function do not exists.
Mar 12 2013
On Tuesday, 12 March 2013 at 16:16:07 UTC, Andrei Alexandrescu wrote:On 3/12/13 11:47 AM, Steven Schveighoffer wrote:s/should/could/ In any case, that's a very dangerous logic to follow. Not-initialized means not initialized. At that point, the concept of empty or not empty is irrelevant, it's a wrong call. By the same token, I don't think anybody would expect a call to empty on a null class reference to actually succeed.On Tue, 12 Mar 2013 11:25:54 -0400, deadalnix <deadalnix gmail.com> wrote:Crossed my mind a few times that fresh non-infinite ranges should be empty.On Tuesday, 12 March 2013 at 10:49:57 UTC, monarch_dodra wrote:Hm... is there a way to test for inifinitness without requiring to be an enum? Wouldn't this also be valid? if(!R.init.empty) Essentially, you can evaluate R.init.empty at compile time AND it's false on an uninitialized range. How can a correctly written non-infinite range pass that? That would make forwarding much easier, as the 'dumb' implementation still would result in an infinite range.Having "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain.Clearly this is a good point. I however think that a static assert within count is much better because it allow to give nicer feedback. The problem with InfiniteRange is that it does gives you cryptic error message like the count function do not exists.But there's no explicit requirement stating that, and I think e.g. one may define a k-elements buffer backed by in-situ storage. AndreiNot sure what you mean?
Mar 12 2013
On Tue, 12 Mar 2013 12:32:22 -0400, monarch_dodra <monarchdodra gmail.com> wrote:On Tuesday, 12 March 2013 at 16:16:07 UTC, Andrei Alexandrescu wrote:No, ranges can be initialized without a constructor. Structs are. Classes aren't. But a class with empty as an enum would work. The idea is that the ultimate underlying source of empty is an enum. Since it's an enum, it should be calculable at compile-time, and it should always be false, regardless of the state of the range (invalid or valid). The problem is whether NON-infinite ranges are empty or not. Looks like there are cases where they could be non-empty.On 3/12/13 11:47 AM, Steven Schveighoffer wrote:s/should/could/ In any case, that's a very dangerous logic to follow. Not-initialized means not initialized. At that point, the concept of empty or not empty is irrelevant, it's a wrong call.On Tue, 12 Mar 2013 11:25:54 -0400, deadalnix <deadalnix gmail.com> wrote:Crossed my mind a few times that fresh non-infinite ranges should be empty.On Tuesday, 12 March 2013 at 10:49:57 UTC, monarch_dodra wrote:Hm... is there a way to test for inifinitness without requiring to be an enum? Wouldn't this also be valid? if(!R.init.empty) Essentially, you can evaluate R.init.empty at compile time AND it's false on an uninitialized range. How can a correctly written non-infinite range pass that? That would make forwarding much easier, as the 'dumb' implementation still would result in an infinite range.Having "isInfinite" can also have the advantage of protecting users from stupid calls. For example, calling "count" on an infinite range is forbidden => shifting problems from runtime to compile time is a HUGE gain.Clearly this is a good point. I however think that a static assert within count is much better because it allow to give nicer feedback. The problem with InfiniteRange is that it does gives you cryptic error message like the count function do not exists.By the same token, I don't think anybody would expect a call to empty on a null class reference to actually succeed.If it's an enum, I would. If it doesn't work, the constraint is false, which is what we want. -Steve
Mar 12 2013
On Tuesday, 12 March 2013 at 16:41:32 UTC, Steven Schveighoffer wrote:On Tue, 12 Mar 2013 12:32:22 -0400, monarch_dodra <monarchdodra gmail.com> wrote:Depends on your definition of "initialized" I guess. Sure, you can create a struct instance without a constructor. Try using a RefCounted!(AutoInit.No) and see what happens.On Tuesday, 12 March 2013 at 16:16:07 UTC, Andrei AlexandrescuNo, ranges can be initialized without a constructor. Structs are. Classes aren't. But a class with empty as an enum would work.Crossed my mind a few times that fresh non-infinite ranges should be empty.s/should/could/ In any case, that's a very dangerous logic to follow. Not-initialized means not initialized. At that point, the concept of empty or not empty is irrelevant, it's a wrong call.The idea is that the ultimate underlying source of empty is an enum. Since it's an enum, it should be calculable at compile-time, and it should always be false, regardless of the state of the range (invalid or valid).Yes. *empty* will always answer false. My point though is that it's not just because range.empty says false that the range is ready for use.The problem is whether NON-infinite ranges are empty or not. Looks like there are cases where they could be non-empty. -SteveI think that each range should be responsible for its own initialization semantics.
Mar 12 2013
On 03/12/2013 05:16 PM, Andrei Alexandrescu wrote:On 3/12/13 11:47 AM, Steven Schveighoffer wrote:std.algorithm.joiner assumes that default-initialized separator ranges are empty. import std.algorithm, std.array, std.range; struct NonEmptyRange{ int[] a = [0,8]; property int front(){ return a.front; } property bool empty(){ return a.empty; } void popFront(){ a.popFront(); } property NonEmptyRange save(){ return NonEmptyRange(a.save); } } void main(){ assert(equal([[1,2,3],[1,2,3]].joiner(NonEmptyRange()),[1,2,3,0,8,1,2,3])); // fail } I'd report it, but the bug tracker is down.... Wouldn't this also be valid? if(!R.init.empty) Essentially, you can evaluate R.init.empty at compile time AND it's false on an uninitialized range. How can a correctly written non-infinite range pass that? That would make forwarding much easier, as the 'dumb' implementation still would result in an infinite range.Crossed my mind a few times that fresh non-infinite ranges should be empty. But there's no explicit requirement stating that, and I think e.g. one may define a k-elements buffer backed by in-situ storage. Andrei
Mar 13 2013
On Tue, 12 Mar 2013, monarch_dodra wrote:One of the advantages is that an infinite range can have random access (meets RA requirements), even though it has no length member (normally, any RA range must have length).Where did this assertion come from? There's nothing about infinite that implies random access in the general case. Consider a circular linked list. It's infinite but not random access. There's a class of infinite functions which are random access, but definitely not all.
Mar 12 2013
On Tuesday, 12 March 2013 at 18:08:25 UTC, Brad Roberts wrote:On Tue, 12 Mar 2013, monarch_dodra wrote:Yeah... ergo "can".One of the advantages is that an infinite range can have random access (meets RA requirements), even though it has no length member (normally, any RA range must have length).Where did this assertion come from? There's nothing about infinite that implies random access in the general case. Consider a circular linked list. It's infinite but not random access. There's a class of infinite functions which are random access, but definitely not all.
Mar 12 2013
On Tue, 12 Mar 2013, monarch_dodra wrote:On Tuesday, 12 March 2013 at 18:08:25 UTC, Brad Roberts wrote:Ok. The use of 'can' there is generally a much stronger implication than that read of it. I read it as an implies rather than allows for the possibility, and I'll bet I'm not alone.On Tue, 12 Mar 2013, monarch_dodra wrote:Yeah... ergo "can".One of the advantages is that an infinite range can have random access (meets RA requirements), even though it has no length member (normally, any RA range must have length).Where did this assertion come from? There's nothing about infinite that implies random access in the general case. Consider a circular linked list. It's infinite but not random access. There's a class of infinite functions which are random access, but definitely not all.
Mar 12 2013
On 10/17/12 8:18 AM, Peter Alexander wrote:2. Make cycle assert when the source range is empty.I think this is sensible. Andrei
Oct 17 2012
On 10/17/2012 5:18 AM, Peter Alexander wrote:So, std.range.isInfinite checks if r.empty is a compile time boolean equal to false. This works most of the time, but not always. Some ranges are infinite, but cannot be determined to be so at compile time (or even run time!) cycle([1, 2, 3]).until(4); // infinite, but not isInfinite cycle(new int[0]); // not infinite, but isInfinite collatzSequence(n).until(1); // infiniteness an open problem! In short, isInfinite does not -- and cannot -- work as a way of telling if a range is finite or infinite in general.isInfinite tells you if it is statically determinable to be infinite, not if it might be at run time.On resolution is to take isInfinite at face value: it only tells you if a range is statically determined to be infinite. If isInfinite is false, it could still be infinite.Right. isInfinite doesn't pretend to solve the halting problem.This leaves us with the tricky cases like cycle(new int[0]). There's three resolutions to this (as far as I can tell): 1. Change cycle to not be an infinite range. 2. Make cycle assert when the source range is empty. 3. Ignore this issue. Option 1 is sound, but kind of sucks, because generally cycle is infinite. Option 2 is sound, but I don't like the idea of asserting on logically valid input just because the problem is too hard.It isn't just hard, it is impossible. Re the halting problem.
Mar 12 2013