Re: Suggested magic for "a" .. "b"
On 2010-07-28 06:54, Martin D Kealey wrote: On Wed, 28 Jul 2010, Michael Zedeler wrote: Writing for ($a .. $b).reverse -> $c { ...} may then blow up because it turns out that $b doesn't have a .succ method when coercing to sequence (where the LHS must have an initial value), just like for $a .. $b -> $c { ... } should be able to blow up because the LHS of a Range shouldn't have to support .succ. Presumably you'd only throw that except if, as well, $b doesn't support .pred ? Yes. It should be .pred. So ($a .. $b).reverse is only possible if $b.pred is defined and $a.gt is defined (and taking an object that has the type of $b.pred). If the coercion to Sequence is taking place first, we'll have to live with two additional constraints ($b.lt and $a.succ), but I guess it would be easy to overload .reverse and get rid of those. Regards, Michael.
Re: Suggested magic for "a" .. "b"
On 2010-07-27 23:50, Aaron Sherman wrote: PS: On a really abstract note, requiring that ($a .. $b).reverse be lazy will put new constraints on the right hand side parameter. Previously, it didn't have to have a value of its own, it just had to be comparable to other values. for example: for $a .. $b -> $c { ... } In that, we don't include the RHS in the output range explicitly. Instead, we increment a $a (via .succ) until it's>= $b. If $a were 1 and $b were an object that "does Int" but just implements the comparison features, and has no fixed numeric value, then it should still work (e.g. it could be random). Now that's not possible because we need to use the RHS a the starting point when .reverse is invoked. This is exactly why I keep writing posts about Ranges being defunct as they have been specified now. If we accept the premise that Ranges are supposed to define a kind of linear membership specification between two starting points (as in math), it doesn't make sense that the LHS has an additional constraint (having to provide a .succ method). All we should require is that both endpoints supports comparison (that they share a common type with comparison, at least). To provide expansion to lists, such as for $a .. $b -> $c { ... }, we should use type coercion semantics, coercing from Range to Sequence and throw an error if the LHS doesn't support .succ. Writing ($a .. $b).reverse doesn't make any sense if the result were a new Range, since Ranges should then only be used for inclusion tests (so swapping endpoints doesn't have any meaningful interpretation), but applying .reverse could result in a coercion to Sequence. Writing for ($a .. $b).reverse -> $c { ...} may then blow up because it turns out that $b doesn't have a .succ method when coercing to sequence (where the LHS must have an initial value), just like for $a .. $b -> $c { ... } should be able to blow up because the LHS of a Range shouldn't have to support .succ. Regards, Michael.
Re: Suggested magic for "a" .. "b"
Aaron Sherman wrote: > As a special case, perhaps you can treat ranges as special and not as simple > iterators. To be honest, I wasn't thinking about the possibility of such > special cases, but about iterators in general. You can't generically reverse > lazy constructs without running afoul of the halting problem, which I invite > you to solve at your leisure ;-) A really obvious example occurs when the RHS is a Whatever: (1..*).reverse; .reverse magic isn't going to be generically applicable to all lazy lists; but it can be applicable to all lazy lists that have predefined start points, end points, and bidirectional iterators, and on all lazy lists that have random-access iterators and some way of locating the tail. Sometimes you can guess what the endpoint and backward-iterator should be from the start point and the forward-iterator, just as the infix:<...> operator is able to guess what the forward-iterator should be from the first one, two, or three items in the list. This is especially a problem with regard to lists generated using the series operator, as it's possible to define a custom forward-iterator for it (but not, AFAICT, a custom reverse-iterator). In comparison, the simplicity of the range operator's list generation algorithm almost guarantees that as long as you know for certain what or where the last item is, you can lazily generate the list from its tail. But only almost: (1..3.5); # list context: 1, 2, 3 (1..3.5).reverse; # list context: 3.5, 2.5, 1.5 - assuming list is generated from tail. (1..3.5).reverse; # list context: 3, 2, 1 - but only if you generate it from the head first, and then reverse it. Again, the proper tool for list generation is the series operator, because it can do everything that the range operator can do in terms of list generation, and more. 1 ... 3.5 # same as 1, 2, 3 3.5 ... 1 # same as 3.5, 2.5, 1.5 - and obviously so. With this in mind, I see no reason to allow any magic on .reverse when dealing with the range operator (or the series operator, for that matter): as far as it's concerned, it's dealing with a list that lacks a reverse-iterator, and so it will _always_ generate the list from its head to its tail before attempting to reverse it. Maybe at some later point, after we get Perl 6.0 out the door, we can look into revising the series operator to permit more powerful iterators so as to allow .reverse and the like to bring more dwimmy magic to bear. -- Jonathan "Dataweaver" Lang
Re: Suggested magic for "a" .. "b"
Sorry I haven't responded for so long... much going on in my world. On Mon, Jul 26, 2010 at 11:35 AM, Nicholas Clark wrote: > On Tue, Jul 20, 2010 at 07:31:14PM -0400, Aaron Sherman wrote: > > > 2) We deny that a range whose LHS is "larger" than its RHS makes sense, > but > > we also don't provide an easy way to construct such ranges lazily > otherwise. > > This would be annoying only, but then we have declared that ranges are > the > > right way to construct basic loops (e.g. for (1..1e10).reverse -> $i > {...} > > which is not lazy (blows up your machine) and feels awfully clunky next > to > > for 1e10..1 -> $i {...} which would not blow up your machine, or even > make > > it break a sweat, if it worked) > > There is no reason why for (1..1e10).reverse -> $i {...} should *not* be > lazy. > > As a special case, perhaps you can treat ranges as special and not as simple iterators. To be honest, I wasn't thinking about the possibility of such special cases, but about iterators in general. You can't generically reverse lazy constructs without running afoul of the halting problem, which I invite you to solve at your leisure ;-) For example, let's just tie it to integer factorization to make it really obvious: # Generator for ranges of sequential, composite integers sub composites(Int $start) { gather do { for $start .. * -> $i { last if isprime($i); take $i; } } } for composites(10116471302318).reverse -> $i { say $i } The first value should be 10116471302380, but computing that without iterating through the list from start to finish would require knowing that none of the integers between 10116471302318 and 10116471302380, inclusive, are prime. Of course, the same problem exists for any iterator where the end condition or steps can't be easily pre-computed, but this makes it more obvious than most. That means that Range.reverse has to do something special that iterators in general can't be relied on to do. Does that introduce problems? Not big ones. I can definitely see people who are used to "for ($a .. $b).reverse -> ..." getting confused when "for @blah.reverse -> ..." blows up their machine, but avoiding that confusion might not be practical. PS: On a really abstract note, requiring that ($a .. $b).reverse be lazy will put new constraints on the right hand side parameter. Previously, it didn't have to have a value of its own, it just had to be comparable to other values. for example: for $a .. $b -> $c { ... } In that, we don't include the RHS in the output range explicitly. Instead, we increment a $a (via .succ) until it's >= $b. If $a were 1 and $b were an object that "does Int" but just implements the comparison features, and has no fixed numeric value, then it should still work (e.g. it could be random). Now that's not possible because we need to use the RHS a the starting point when .reverse is invoked. I have no idea if that matters, but it's important to be aware of when and where we constrain the interface rather than discovering it later. -- Aaron Sherman Email or GTalk: a...@ajs.com http://www.ajs.com/~ajs