I noticed that in my Ceylon code most "for" loops that use the x..y
notation look like this:
if (count > 0) {
for (i in start..(start+count-1).natural) {
// ...
}
}
Not very nice. Is there a better way to write this?
Ivo
if (count > 0) {
for (i in start..start+count.predecessor) {
// ...
}
}
But I've been toying with adding some kind of object to produce ranges
by start and length (instead of from/to). Then the idiom would be:
for (i in segment(start,count)) {
// ...
}
We've even floated the idea of providing an operator for this, something like:
for (i in start..<count) {
// ...
}
But I'm not really warming to that idea. I've seen it in other
languages and never really loved it.
But anyway, my guess is that you're really trying to iterate over a
segment of a sequence or string or something like that, in which case
iterating over indexes is simply never the correct idiom in Ceylon.
The correct approach is always this:
for (x in sequence.segment(start,count)) {
// ...
}
Basically, we almost *never* iterate over indexes in Ceylon, which is
why Ceylon doesn't support a C-style "for".
--
Gavin King
gavin...@gmail.com
http://in.relation.to/Bloggers/Gavin
http://hibernate.org
http://seamframework.org
But the bigger question is: is it really the right thing to say that
natural-natural results in an Integer?
On Fri, Dec 9, 2011 at 11:39 AM, Tako Schotanus <ta...@codejive.org> wrote:
> Personally I could still live with start..(start+count-1) but it's the
> .natural that makes it ugly to me, it seems one of those places where the
> compiler should just be able to "understand" you.
> Also, why's there a ".natural"? Does that round up, or does it do absolute
> value?
I recently decided that "x-y" should result in an Integer when x and y
are both Natural, rather than resulting in an exception when x<y. This
is not a decision I'm especially confident about, but it seems more
in line with our whole philosophy. So you need to explicitly call
(x-y).natural if you want to cast the Integer back down to a Natural,
risking an exception.
That's exactly the point.
The real problem with "for (i in x..y)" is: it is potentially
dangerous.
1. When the bounds are not literals, it is easy to come to the
impression that it will also work for the case where you would have 0
iterations. But actually, x..y can never result in less than one
iteration, because in the case where you might expect 0 iterations,
you really get 2 iterations in the other direction.
2. If x is 0, you get the problem described by Ross.
3. When writing the program, the author almost always knows in which
direction the loop is supposed to iterate. So it is not helpful but
actually harmful that the direction of iteration can automatically
turn around if y happens to be less than x.
Ok, you can probably avoid these problems by using something like
segments. But as long as "x..y" is the most natural syntax available
for iterating over a range of numbers, many people will use it for
that purpose - even in cases where a different (but less obvious)
construct would possible have been better. Something so seemingly
obvious should just not backfire that easily!
Concerning x-y potentially throwing an exception vs. x-y resulting in
an Integer (and num.natural potentially throwing an exception):
The latter is clearly better IMO, because otherwise a-b+c and a+c-b
are not equivalent anymore, which seems rather counter-intuitive.
I recently decided that "x-y" should result in an Integer when x and y
are both Natural, rather than resulting in an exception when x<y. This
is not a decision I'm especially confident about, but it seems more
in line with our whole philosophy. So you need to explicitly call
(x-y).natural if you want to cast the Integer back down to a Natural,
risking an exception.
> I'm not a big fan of that one, I think it's trying to solve the unsolvable.
So then the bigger question is: is it even worth having Natural? Or
should we just eliminate it?
To me it serves a really nice *documentation* purpose in the very
common case when a parameter only accepts positive numbers and zero.
But if its just going to get in people's way and annoy them, without
adding any real extra typesafety, I can live without it.
On Fri, Dec 9, 2011 at 3:24 PM, Stephane EpardaudSo then the bigger question is: is it even worth having Natural? Or
<stephane...@gmail.com> wrote:
> I'm not a big fan of that one, I think it's trying to solve the unsolvable.
should we just eliminate it?
To me it serves a really nice *documentation* purpose in the very
common case when a parameter only accepts positive numbers and zero.
But if its just going to get in people's way and annoy them, without
adding any real extra typesafety, I can live without it.
There are several downsides of Natural:
* the need to prefix integer literals with + as in +10
* the simple complexity of having 5 numeric types instead of 4
* the annoyance that Natural is not automatically widened to
Integer in invocations and return statements
* the fact that it's unfamiliar and potentially intimidating and will likely
cause problems and confusion for beginners
* it slightly complexifies Java interop
That's not a short list.
I'm worried that all the accumulated unfamiliarity of Foo vs Foo?,
Empty vs Sequence<Foo> and Integer vs Natural is going to be
just a little too much for people taking their first steps with this
language.
> Personally it has always annoyed me, beginning with having to write +2, +3
> etc instead of plain 2 and 3.
> But I could live with that if at least the conversions from one to the other
> would be automatic in most cases (using a Natural in case of an Integer
> should always be possible for example)
Right. We're trying to create a language that is *less* irritating that Java.
> Is the list of benefits of Natural really long enough to justify its existence?
At this point it's pretty hard to know for sure, but it's beginning to
feel like the answer is "no".
It would not bother me so much if it weren't for the fact that
sequences pretty much force you into using Natural for certain basic
things.
There's little we can do with respect to this kind of stuff if we want
high performance on the JVM. Because at the end these things get
optimized down to a Java long or double.
--
> Ok, you can probably avoid these problems by using something like
> segments. But as long as "x..y" is the most natural syntax available
> for iterating over a range of numbers, many people will use it for
> that purpose - even in cases where a different (but less obvious)
> construct would possible have been better. Something so seemingly
> obvious should just not backfire that easily!
So in my experience there are two common cases:
1. you are given a startpoint and endpoint and you want to iterate
over them inclusively, or
2. you are given a startpoint and a length, and you want exactly
length elements.
I'm calling 1 a "span" and 2 a "segment", though perhaps someone has
seen better terminology for these things.
Common C idiom confuses the two usecases and we wind up with a
"djikstra span" where you are given a startpoint and an endpoint and
want everything between then including the startpoint and excluding
the endpoint. The thing we write as [x,y) in mathematics. To my mind,
a "djikstra span" is a far less naturally occurring thing than either
1 or 2 above, and that's where you're getting your "start+length-1"
mess from. Now, this idiom is quite deeply ingrained in the C family
of languages, but I don't think it's actually the most convenient
thing in most usecases.
Now, one idea I've been toying with since reading you're email is
using : as the operator for creating a segment of ordinal values. I
don't know why, but I find that the following reads really naturally
to me:
for (i in 0:length) { ... }
I don't know if this appeals to anyone else...
> Now, one idea I've been toying with since reading you're email is
> using : as the operator for creating a segment of ordinal values. I
> don't know why, but I find that the following reads really naturally
> to me:
>
> for (i in 0:length) { ... }
>
> I don't know if this appeals to anyone else...
And of course
sequence[0:n]
would give you the first n elements of a sequence.
sequence[sequence.lastIndex:-n]
might give you the last n elements.
> Now, one idea I've been toying with since reading you're email is
"your email" dammit!
Cool! I like it. And it has the advantage that people don't end up using x..y just because it looks more simple.
The distinction between span and segment is clear and logical. But the syntax should not create an unjustified preference for segments over spans. Having syntactic sugar for both solves that problem.
Not sure about this myself, just trying to figure out if there could be a compromise:
What about removing Natual as a separate data type and instead introducing an annotated integer like "natural Integer" (and possibly even "Natural" as a shortcut for that). The compiler would then insert a check for x>=0 every time a value is stored in a "natural Integer". So you could write something like:
natural Integer x = a - b * (c - 1);
And even if a, b and c are also all "natural Integer", the only point where a check would be performed is when assigning the result of the calculation to x.
Of course, no check would be necessary when assigning one "natural Integer" to another. So you would basically have a "light" version of the old Natural.
True. But I'm not sure if interceptors would be a practicable solution here. The tricky part would not be to insert the checks but to figure out when *not* to insert a check. If even a simple x++ results in a check for x>=0, possibly even through an additional method call, the whole concept becomes so inefficient that it would be rendered useless.
> for (i in 0:length) { ... }
Another potential syntax could be:
for (i in 0../length) { ... }
sequence[0../n]
However at this point we start getting into cryptic ascii art territory.
Perhaps you're right, but there might be a way to make it work out.
On Fri, Dec 9, 2011 at 8:57 PM, Tako Schotanus <ta...@codejive.org> wrote:
> I'd say go ahead, I will not miss it.
>
> -Tako
>
>
>
> On Sat, Dec 10, 2011 at 03:49, Gavin King <gavin...@gmail.com> wrote:
>>
>> Frankly I'm ready to go ahead and remove Natural right now if that is
>> the consensus of the other folks here. It doesn't really add very much
>> in terms of type safety - a little, perhaps - and what it does add is
>> probably outweighed by the inconvenience and plain
>> getting-in-the-wayness of having to convert between integers and
>> naturals. I don't want to have a thing like this of marginal value
>> that is a turnoff for beginners, and continues to be minor irritation
>> to those who are accustomed to it, if we can help it. I mean, if we
>> remove it now, I doubt that anybody will every really miss it, and I'm
>> sure we will avoid lots and lots of complaints.
https://github.com/ceylon/ceylon-spec/issues/99
http://ceylon-lang.org
http://hibernate.org
http://seamframework.org
I'd rather have a constraint annotation for this "documentation" purpose.