Dart Language and Library Newsletter (2017-11-10)

234 views
Skip to first unread message

Florian Loitsch

unread,
Nov 10, 2017, 1:13:18 PM11/10/17
to General Dart Discussion
I will be traveling next week. The next newsletter is therefore only in two weeks.



Dart Language and Library Newsletter

Welcome to the Dart Language and Library Newsletter.

Did You Know?

Constructors

Dart has many ways to make writing constructors easier or more powerful. The most known is probably the concise syntax for initializing instance fields directly in the signature line (see below). This section shows some other, less known features.

// Concise syntax for initializing fields while declaring parameters.
class A {
  final int x;
  A(this.x);
}

Generative Constructors

A constructor is "generative", if it is called on a freshly created instance to initialize the object. This sounds complicated, but just describes the behavior of the most common constructors.

class A {
  int x;
  A(int y) : this.x = y + 2;
}

When a user writes new A(), conceptually, the program first instantiates an uninitialized object of type A, and then lets the constructor initialize it (set the field x).

The reason for this wording is, that generative constructors can be used in super calls in initializer lists. When called as super the generative constructor doesn't instantiate a new object again. It just does its part of the initialization.

class A {
  int x;
  A(int y) : this.x = y + 2;
}

class B extends A {
  B(int z) : super(z - 1) {
    print("in B constructor");
  }
}

The order of evaluation is well defined: first all expressions in the initializer list are evaluated. Then the initializer list of the super constructor is run. This continues, until Object (the superclass of every class) is reached. Then, the bodies of the constructors are executed in reverse order, first starting the one from Object (not doing anything), and working its way down the class hierarchy.

This evaluation order is usually not noticeable, but can be important when the expressions have side-effects, and/or the bodies read final fields:

int _counter = 0;

class A {
  final int aCounter;
  A() : aCounter = _counter++ {
    print("foo: ${foo()}");
  }
}

class B extends A {
  final int bCounter;
  final int field;

  B()
      : field = 499,
        bCounter = _counter++ {
    print("B");
  }

  int foo() => field;
}

main() {
  var b = new B();
  print("aCounter: ${b.aCounter}");
  print("bCounter: ${b.bCounter}");
}

Running this program yields:

foo: 499
B
aCounter: 1
bCounter: 0

Note that the bCounter expression is evaluated first, yielding 0, and that aCounter, coming second, is set to 1. Furthermore, the final field field in B is set to 499 when the constructor in A indirectly accesses the field.

Dart guarantees that final fields are only visible with their final value. Dart ensures this property by splitting the construction of objects into two: the initializer list, and the constructor body. Without this two-phase initialization Dart wouldn't be able to provide this guarantee.

Factory Constructors

Factory constructors are very similar to static functions, except that they can be invoked with new. They don't work on an instantiated (uninitialized) object, like generative constructors, but they must create the object themselves.

The following example shows how Future.microtask could be implemented with a factory and the existing Completerclass.

class Future<T> {
  factory Future.microtask(FutureOr<T> computation()) {
    Completer c = new Completer<T>();
    scheduleMicrotask(() { ... c.complete(computation()) ... });
    return c.future;
  }
}

The actual implementation uses private classes to be more efficient, but is otherwise very similar to this code.

Factory constructors cannot be used as targets of super in initializers. (This also means that a class that only has factory constructors cannot be extended).

Redirecting Generative Constructor

When constructors want to share code it is often convenient to just forward from one constructor to another one. This can be achieved with factory constructors, but if the constructor should also be usable as the target of a super-initializer call, then factory constructors (as described above) are not an option. In this case, one has to use redirecting generative constructors:

class Point {
  final int x;
  final int y;
  Point(this.x, this.y);
}

class Rectangle {
  int x0;
  int y0;
  int x1;
  int y1;

  Rectangle.coordinates(this.x0, this.y0, this.x1, this.y1);

  Rectangle.box(Point topLeft, int width, int height)
      : this.coordinates(topLeft.x, topLeft.y, topLeft.x + width, topLeft.y.height);
}

class Square extends Rectangle {
  Box(Point topLeft, int width) : super.box(topLeft, width, width);
}

The Rectangle class has two constructors (both generative): coordinates and box. The box constructor redirects to the coordinates constructor.

As can be seen, a subtype, here Square, can still use the constructor in the initializer list.

Redirecting Factory Constructors

Frequently, factory constructors are just used to instantiate a differently named class. For example, the Iterable class is actually abstract and a new Iterable.empty() can't therefore be generative but must be a factory. With factory constructors this could be implemented as follows:

abstract class Iterable<E> {
  factory Iterable.empty() {
    return new _EmptyIterable<E>();
  }
}

There are two reasons, why we are not happy with this solution:

  1. there is an unnecessary redirection: the compilers need to inline the factory constructor, instead of seeing directly that a new Iterable.empty() should just directly create an _EmptyIterable. (Our compilers inline these simple constructors, so this is not a real problem in practice).
  2. A factory constructor with a body cannot be const. Clearly, there is code being executed (even if it's just new _EmptyIterable()), which is not allowed for const constructors.

The solution is to use redirecting factory constructors:

abstract class Iterable<E> {
  const factory Iterable.empty() = _EmptyIterable<E>;
}

Now, the Iterable.empty() constructor is just a synonym for _EmptyIterable<E>. Note that we don't even need to provide arguments to the _EmptyIterable<E> constructor. They must be the same as the one of the redirecting factory constructor.

Another example:

class C {
  final int x;
  final int y;
  const C(this.x, this.y);
  factory const C.duplicate(int x) = _DuplicateC;
}

class _DuplicateC implements C {
  final int x;
  int get y => x;
  const _DuplicateC(this.x);
}

Shorter Final Variables

In Dart it is now easier to declare mutable locals, than to declare immutable variables:

var mutable = 499;
final immutable = 42;

Declaring a variable as mutable, but not modifying it, isn't a real problem per se, but it would be nice, if the var keyword actually expressed the intent that the variable will be modified at a later point.

We recently looked at different ways to make immutable locals more appealing. This section contains our proposal.

Instead of using a different keyword (like val) we propose to use an even shorter syntax for immutable locals: colon-equals (:=).

In this proposal, a statement of the form identifier := expression; introduces a new final local variable.

  // DateTime.toString() method.
  String toString() {
    y := _fourDigits(year);
    m := _twoDigits(month);
    d := _twoDigits(day);
    h := _twoDigits(hour);
    min := _twoDigits(minute);
    sec := _twoDigits(second);
    ms := _threeDigits(millisecond);
    us := microsecond == 0 ? "" : _threeDigits(microsecond);
    if (isUtc) {
      return "$y-$m-$d $h:$min:$sec.$ms${us}Z";
    } else {
      return "$y-$m-$d $h:$min:$sec.$ms$us";
    }
  }

As a first reaction, it feels dangerous to just use one character (":") to introduce a new variable. In our experiments this was, however, not an issue. In fact, single-character modifiers of = are already common: x += 3 is also just one character on top of = and we are not aware of any readability issues with compound assignments. Furthermore, syntax highlighting helps a lot in ensuring that these variable declarations aren't lost in the code.

We would also like to support typed variable declarations: Type identifier := expression. (The following examples are just random variable declarations of our codebase that have been rewritten to use the new syntax).

int pos := value.indexOf(":");
JSSyntaxRegExp re := pattern;
IsolateEmbedderData ied := isolateEmbedderData.remove(portId);

For now, we are only looking at the := syntax for local variables. If it proves to be successful, we will investigate whether we should allow the same syntax for final (global) statics or fields.

For Loops

For loops are another place where users frequently declare new variables. There, we need to pay a bit more attention. For example, the for-in statement doesn't even have any assignment symbol, which we could change to :=.

When looking at uses of for-in, we found that these loops are almost never used without introducing a loop variable:

var x;
for (x in [1, 2]) {
  print(x);
}

In fact, the only cases where we found this pattern was in our own tests...

We thus propose to change the meaning of for (identifier in Iterable). It should become syntactic sugar for for (final identifier in Iterable).

Note that Dart already supports final identifier in for-in loops, since each iteration has its own variable. This can be seen in the following example:

main() {
  var funs = [];
  for (final x in [1, 2, 3]) {  // With or without `final`.
    funs.add(() => x);
  }
  funs.forEach((f) => print(f()));  // => 1 2 3
}

With the new syntax the final keyword wouldn't be necessary in this example.

Finally, we also had a look at for. Similar to for-in, a for loop, already now, does not reuse the loop variable, but introduces a fresh variable for each iteration.

main() {
  var funs = [];
  for (int i = 0; i < 3; i++) {
    funs.add(() => i);
  }
  funs.forEach((f) => print(f()));  // => 0 1 2
}

This means that there is already syntactic sugar happening to make this happen. It is thus relatively straightforward to support a version where a loop variable introduced with := is final within the body of the loop.

main() {
  var funs = [];
  for (i := 0; i < 3; i++) {
    funs.add(() => i);
  }
  funs.forEach((f) => print(f()));  // => 0 1 2
}

This would be (roughly) equivalent to:

main() {
  var funs = [];
  var i_outer;
  for (i_outer = 0; i_outer < 3; i_outer++) {
    i_inner := i_outer;
    funs.add(() => i_inner);
  }
  funs.forEach((f) => print(f()));  // => 0 1 2
}

Summary

We are investigating ways to make the declaration of final locals easier. In this proposal we suggest the use of := as new syntax to concisely declare a fresh final local.

We also propose changes to the for and for-in statements to make the declaration of final variables concise. The for loop would support the := syntax, and a for-in statement without var or type would implicitly introduce a fresh final variable.

tatumizer-v0.2

unread,
Nov 10, 2017, 4:30:36 PM11/10/17
to Dart Misc
Would it be prudent to make another step in the same direction so that we can write
for (i := 0; i < 3; i++)
simply as
for (i :<3)
(colon followed by <)
The point is that 90+% of all loops are like this - why do we need an extra noise, which only invites  bugs?

Danny Tuppeny

unread,
Nov 12, 2017, 5:51:28 AM11/12/17
to mi...@dartlang.org
Wouldn't it be more readable to just have a range operator?

for (i in 1..3) {
}

If you've never seen either of these, I think the range operator is much more obvious what it does and although it's a few more characters it also allows you to choose the starting value (eg. with :< do you start at 0 or 1? 0 might be better if indexing into an array, but 1 might be better if you're rendering something for a user).

Also - if for loops only invite bugs, why not just remove them? ;-)

Man Hoang

unread,
Nov 13, 2017, 2:49:10 AM11/13/17
to Dart Misc
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Erik Ernst

unread,
Nov 13, 2017, 3:54:59 AM11/13/17
to Dart Misc
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.



--
Erik Ernst  -  Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 København K, Denmark
CVR no. 28866984

Man Hoang

unread,
Nov 13, 2017, 4:07:36 AM11/13/17
to Dart Misc
I mean the for..in loop.

Erik Ernst

unread,
Nov 13, 2017, 4:48:49 AM11/13/17
to Dart Misc
On Mon, Nov 13, 2017 at 10:07 AM, Man Hoang <joll...@gmail.com> wrote:
I mean the for..in loop.

Ah, right, but even there you could say that it is explicit: _Every_ `for` declares an iteration variable, so the reserved word `for` serves as a marker that says "declaration ahead!".

Philipp S

unread,
Nov 13, 2017, 6:03:08 AM11/13/17
to Dart Misc
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

But I really like `final` variables in general, and I think the idea to have them for `for` iteration counters is great! What do you think of something along these lines: Iff a `for` loop introduces a variable `i` in its initializer expression as final, then it is already final in the check and increment expressions, and both must not mutate `i`; After each iteration, `i` gets assigned the result of evaluating increment.
Printing the numbers 0..9 would then look like:
for (i := 0; i < 10; i + 1) print(i);

Erik Ernst

unread,
Nov 13, 2017, 6:19:05 AM11/13/17
to Dart Misc
On Mon, Nov 13, 2017 at 12:03 PM, Philipp S <philippsc...@gmail.com> wrote:
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

The appropriate intuition here is that each step of the iteration gets its own copy of the iteration variable (so `i` during the first iteration is simply a different variable than `i` during the second iteration). So increment doesn't write to the same final variable at the beginning of each step, it initializes a fresh final variable.

This is required in order to make function closures that capture the iteration variable see the value of the iteration where the capture occurred (even if it is called after the entire loop has terminated). This is the reason why so many examples use function literals (like () => i) to show how the iteration variable works. If a single variable had been used for the entire loop execution then all those closures would have returned the latest value that this variable had, so the printout would have been `2 2 2` rather than `0 1 2`.

This is not an accident: The language specification section 17.6 explains the semantics in terms of a fresh variable per iteration, exactly because of the confusion which is traditionally caused by captured iteration variables that end up having "the latest value" when such a closure is invoked.

But I really like `final` variables in general, and I think the idea to have them for `for` iteration counters is great! What do you think of something along these lines: Iff a `for` loop introduces a variable `i` in its initializer expression as final, then it is already final in the check and increment expressions, and both must not mutate `i`; After each iteration, `i` gets assigned the result of evaluating increment.
Printing the numbers 0..9 would then look like:
for (i := 0; i < 10; i + 1) print(i);

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Erik Ernst

unread,
Nov 13, 2017, 6:31:36 AM11/13/17
to Dart Misc
On Mon, Nov 13, 2017 at 12:19 PM, Erik Ernst <eer...@google.com> wrote:
On Mon, Nov 13, 2017 at 12:03 PM, Philipp S <philippsc...@gmail.com> wrote:
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

The appropriate intuition here is that each step of the iteration gets its own copy of the iteration variable (so `i` during the first iteration is simply a different variable than `i` during the second iteration). So increment doesn't write to the same final variable at the beginning of each step, it initializes a fresh final variable.

This is required in order to make function closures that capture the iteration variable see the value of the iteration where the capture occurred (even if it is called after the entire loop has terminated). This is the reason why so many examples use function literals (like () => i) to show how the iteration variable works. If a single variable had been used for the entire loop execution then all those closures would have returned the latest value that this variable had, so the printout would have been `2 2 2` rather than `0 1 2`.

This is not an accident: The language specification section 17.6 explains the semantics in terms of a fresh variable per iteration, exactly because of the confusion which is traditionally caused by captured iteration variables that end up having "the latest value" when such a closure is invoked.

Actually, the language specification does not spell out how an expression like `i++` must be considered to mean `ik+1 = ik + 1`. We will fix that.

But I really like `final` variables in general, and I think the idea to have them for `for` iteration counters is great! What do you think of something along these lines: Iff a `for` loop introduces a variable `i` in its initializer expression as final, then it is already final in the check and increment expressions, and both must not mutate `i`; After each iteration, `i` gets assigned the result of evaluating increment.
Printing the numbers 0..9 would then look like:
for (i := 0; i < 10; i + 1) print(i);

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.



--
Erik Ernst  -  Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 København K, Denmark
CVR no. 28866984

Philipp S

unread,
Nov 13, 2017, 7:48:56 AM11/13/17
to Dart Misc
Am Montag, 13. November 2017 12:19:05 UTC+1 schrieb Erik Ernst:
On Mon, Nov 13, 2017 at 12:03 PM, Philipp S <philippsc...@gmail.com> wrote:
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

The appropriate intuition here is that each step of the iteration gets its own copy of the iteration variable (so `i` during the first iteration is simply a different variable than `i` during the second iteration). So increment doesn't write to the same final variable at the beginning of each step, it initializes a fresh final variable.

Sure, I get what the language does and why it is necessary. I see why a shared counter variable would be a bad idea. Even though I've never consciously thought about it before, the distinction between variables i_{k} and i_{k+1} is what my subconsciousness always expected to happen, as your example demonstrates.

I think the definition of counter variables with `:=` is different. Now, you ask the compiler for an immutable variable `i`, and that's what you get. You're not allowed to assign to `i`. Except for one magic place, the loop expressions, where assignment to `i` is allowed. This "upgrading a final variable to a non-final one" is a completely new concept in Dart that I've never encountered before. This unfamiliarity makes it difficult for me to predict it's exact functionality and limits.
For example, I couldn't confidently answer the question: "is the check expression allowed to mutate `i`?" or "Can a final variable be promoted to non-final if it was not declared in the loop initializer? (probably not)"
But maybe that's just me :-)

Philipp S

unread,
Nov 13, 2017, 8:00:50 AM11/13/17
to Dart Misc
I'm just trying to say: Make it more transparent where the expression i_{k+1} = i_{k} is allowed. When does `i` refer to i_{k}, and when to i_{k+1}? Sorry for the confusion.

Erik Ernst

unread,
Nov 13, 2017, 8:10:15 AM11/13/17
to Dart Misc
On Mon, Nov 13, 2017 at 1:48 PM, Philipp S <philippsc...@gmail.com> wrote:
Am Montag, 13. November 2017 12:19:05 UTC+1 schrieb Erik Ernst:
On Mon, Nov 13, 2017 at 12:03 PM, Philipp S <philippsc...@gmail.com> wrote:
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

The appropriate intuition here is that each step of the iteration gets its own copy of the iteration variable (so `i` during the first iteration is simply a different variable than `i` during the second iteration). So increment doesn't write to the same final variable at the beginning of each step, it initializes a fresh final variable.

Sure, I get what the language does and why it is necessary. I see why a shared counter variable would be a bad idea. Even though I've never consciously thought about it before, the distinction between variables i_{k} and i_{k+1} is what my subconsciousness always expected to happen, as your example demonstrates.

The shared variable could still be expressed, and I suppose none of the upcoming language changes would prevent the following:

main() {
  var functions = <int Function()>[];
  int i = 3;

  for (/* nothing to do here */; i > 0; i--) {
    functions.add(() => i);
  }
  functions.forEach((f) => print(f())); // Prints '0' each time.
}
 
But I'd expect that the use of an already-defined variable (which also implies "the same variable for all steps of the iteration") as a `for` iteration variable would be considered error-prone, probably just something to 'lint' away.

I think the definition of counter variables with `:=` is different.

Apart from the treatment of `const`, I'd expect `<name> := <expression>` to work like `final <name> := <expression>`.
 
Now, you ask the compiler for an immutable variable `i`, and that's what you get.

But the iteration variable should actually be immutable. (And the spec needs to be adjusted such that it does not pretend that we could do `i++` when `i` is `final`).
 
You're not allowed to assign to `i`. Except for one magic place, the loop expressions, where assignment to `i` is allowed.

That, again, should be specified to mean something where no assignments to final variables occur.
 
This "upgrading a final variable to a non-final one" is a completely new concept in Dart that I've never encountered before.

And you shouldn't ever encounter it.
 
This unfamiliarity makes it difficult for me to predict it's exact functionality and limits.
For example, I couldn't confidently answer the question: "is the check expression allowed to mutate `i`?" or "Can a final variable be promoted to non-final if it was not declared in the loop initializer? (probably not)"
But maybe that's just me :-)

And me. ;-)

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

tatumizer-v0.2

unread,
Nov 13, 2017, 10:17:45 AM11/13/17
to Dart Misc
Range in current form, as in 
for (i in [0..3])
is error-prone, too. Notice this "3"? i will never be assigned 3 (if I'm not mistaken), b/c this range is exclusive. It's easy to become confused on whether it's exclusive or inclusive (BTW, do we have 3 dots for inclusive? I don't remember)
Not sure why there's such opposition to 
for (i:<3)
The meaning is absolutely clear, and can't be mistaken for anything else. If you omit colon, compiler will complain (even if i is declared in outer scope) - expression will become invalid.
The only argument I see is that this form is not found in java or C# :)

Randal L. Schwartz

unread,
Nov 13, 2017, 12:16:55 PM11/13/17
to Danny Tuppeny, mi...@dartlang.org
>>>>> "Danny" == Danny Tuppeny <da...@tuppeny.com> writes:

Danny> Wouldn't it be more readable to just have a range operator?

Danny> for (i in 1..3) {
Danny> }

I'd rather see Iterable<int> upTo(int i) as an int method, so that the
parallel .downTo is also available. And that would require no language
change, just some more methods.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<mer...@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/Dart consulting, Technical writing, Comedy, etc. etc.
Still trying to think of something clever for the fourth line of this .sig

Florian Loitsch

unread,
Nov 13, 2017, 12:19:29 PM11/13/17
to mi...@dartlang.org, Danny Tuppeny
We discussed "x.to(y)", but our experiments showed that most users would misinterpret the "y"'s inclusiveness depending on the context. Depending on where it was used, readers either expected the 'y' to be part of the range, or not.


Florian Loitsch

unread,
Nov 13, 2017, 12:23:22 PM11/13/17
to mi...@dartlang.org
On Mon, Nov 13, 2017 at 3:31 AM 'Erik Ernst' via Dart Misc <mi...@dartlang.org> wrote:
On Mon, Nov 13, 2017 at 12:19 PM, Erik Ernst <eer...@google.com> wrote:
On Mon, Nov 13, 2017 at 12:03 PM, Philipp S <philippsc...@gmail.com> wrote:
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

The appropriate intuition here is that each step of the iteration gets its own copy of the iteration variable (so `i` during the first iteration is simply a different variable than `i` during the second iteration). So increment doesn't write to the same final variable at the beginning of each step, it initializes a fresh final variable.

This is required in order to make function closures that capture the iteration variable see the value of the iteration where the capture occurred (even if it is called after the entire loop has terminated). This is the reason why so many examples use function literals (like () => i) to show how the iteration variable works. If a single variable had been used for the entire loop execution then all those closures would have returned the latest value that this variable had, so the printout would have been `2 2 2` rather than `0 1 2`.

This is not an accident: The language specification section 17.6 explains the semantics in terms of a fresh variable per iteration, exactly because of the confusion which is traditionally caused by captured iteration variables that end up having "the latest value" when such a closure is invoked.

Actually, the language specification does not spell out how an expression like `i++` must be considered to mean `ik+1 = ik + 1`. We will fix that.

That's because `i++` only works on one instance of the variable.
The syntax expansion of a for-loop never splits the visibility of a variable inside an update clause.
This can be observed by capturing the loop variable in the condition and/or update clause. Then it becames clear which variables are captured.

As part of the `:=` proposal we would also like to simplify this expansion: capturing inside the condition and update clause would capture a loop variable (and not the body variables as is currently the case). 

Randal L. Schwartz

unread,
Nov 13, 2017, 12:43:22 PM11/13/17
to 'Florian Loitsch' via Dart Misc, Danny Tuppeny
>>>>> "Florian" == 'Florian Loitsch' via Dart Misc <mi...@dartlang.org> writes:

Florian> We discussed "x.to(y)", but our experiments showed that most users would
Florian> misinterpret the "y"'s inclusiveness depending on the context. Depending on
Florian> where it was used, readers either expected the 'y' to be part of the range,
Florian> or not.

.upToIncluding(y)
.upToExcluding(y)

and the corresponding .downTo*

Or maybe .upTo is always including, and provide .upToExcluding as the
clear alternative.

tatumizer-v0.2

unread,
Nov 13, 2017, 1:02:18 PM11/13/17
to Dart Misc, da...@tuppeny.com
Or just 
for (i in [0..<5])
?

Danny Tuppeny

unread,
Nov 13, 2017, 1:12:14 PM11/13/17
to mi...@dartlang.org
On Mon, 13 Nov 2017 at 15:17 tatumizer-v0.2 <tatu...@gmail.com> wrote:
Range in current form, as in 
for (i in [0..3])
is error-prone, too. Notice this "3"? i will never be assigned 3 (if I'm not mistaken), b/c this range is exclusive. It's easy to become confused on whether it's exclusive or inclusive (BTW, do we have 3 dots for inclusive? I don't remember)

I expected .. to be inclusive (in Perl it was inclusive? not sure who else has it). You say "in current form" as if this already exists? Have I missed this? =(

 
Not sure why there's such opposition to 
for (i:<3)
The meaning is absolutely clear, and can't be mistaken for anything else

I don't agree with this; it seems pretty cryptic to me.


On Mon, 13 Nov 2017 at 17:16 Randal L. Schwartz <mer...@stonehenge.com> wrote:
Danny> Wouldn't it be more readable to just have a range operator?

Danny> for (i in 1..3) {
Danny> }

I'd rather see Iterable<int> upTo(int i) as an int method, so that the
parallel .downTo is also available.  And that would require no language
change, just some more methods.

This only supports a fixed start though, it'd probably be better as range(int x, int y).. But C# has an Enumerable.Range which takes two args where the second is the *count* and it's mighty confusing (works good if you pass array.length, but why would you enumerate an array this way?). If Dart had a range method that worked differently, I think that'd also be confusing (but I wouldn't advocate implementing it the same!).

I'm curious what the use cases are to have an exclusive range? I can only think of arrays because of zero-indexing and there are better ways to enumerate them - I don't think people would start writing "for (i in 0..array.Length)" so with an inclusive range operator be a problem?

Danny Tuppeny

unread,
Nov 13, 2017, 1:14:38 PM11/13/17
to mi...@dartlang.org
On Mon, 13 Nov 2017 at 18:11 Danny Tuppeny <da...@tuppeny.com> wrote:
This only supports a fixed start though, it'd probably be better as range(int x, int y)..

I realise I misread the suggestion now - I guess it meant 10.upTo(20) rather than int.upTo(20).

Feels a bit weird to me having an Iterable<int>-returning method hanging off an int though.

Florian Loitsch

unread,
Nov 13, 2017, 1:51:16 PM11/13/17
to mi...@dartlang.org, Danny Tuppeny
On Mon, Nov 13, 2017 at 9:43 AM Randal L. Schwartz <mer...@stonehenge.com> wrote:
>>>>> "Florian" == 'Florian Loitsch' via Dart Misc <mi...@dartlang.org> writes:

Florian> We discussed "x.to(y)", but our experiments showed that most users would
Florian> misinterpret the "y"'s inclusiveness depending on the context. Depending on
Florian> where it was used, readers either expected the 'y' to be part of the range,
Florian> or not.

.upToIncluding(y)
.upToExcluding(y)

and the corresponding .downTo*

Or maybe .upTo is always including, and provide .upToExcluding as the
clear alternative.

But then it's so long that it doesn't really buy you a lot anymore. At that point you might as well just write a `for` loop. 

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<mer...@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/Dart consulting, Technical writing, Comedy, etc. etc.
Still trying to think of something clever for the fourth line of this .sig

Randal L. Schwartz

unread,
Nov 13, 2017, 2:27:39 PM11/13/17
to Danny Tuppeny, mi...@dartlang.org
>>>>> "Danny" == Danny Tuppeny <da...@tuppeny.com> writes:

Danny> Feels a bit weird to me having an Iterable<int>-returning method hanging
Danny> off an int though.

Any worse than String.split?

Philipp S

unread,
Nov 13, 2017, 2:35:06 PM11/13/17
to Dart Misc, da...@tuppeny.com

We discussed "x.to(y)", but our experiments showed that most users would misinterpret the "y"'s inclusiveness depending on the context. Depending on where it was used, readers either expected the 'y' to be part of the range, or not.

When talking about ranges, two other use cases come to my mind.
1. bounds checks: checking `i ∈ [0, n)` looks imo better with `const Range(0, n).contains(i)` than with `0 <= i && i < n`
2. enumerating other types than int: `for (final day in new Range.inclusive(startDay, endDay, stepsize: const Duration(days: 1)))`

Do you have any experiences to share regarding something like a `class Range<T extends Comparable<T>> extends Set<T>`? 

Philipp S

unread,
Nov 13, 2017, 3:09:03 PM11/13/17
to Dart Misc
That's because `i++` only works on one instance of the variable.
The syntax expansion of a for-loop never splits the visibility of a variable inside an update clause.
This can be observed by capturing the loop variable in the condition and/or update clause. Then it becames clear which variables are captured.

As part of the `:=` proposal we would also like to simplify this expansion: capturing inside the condition and update clause would capture a loop variable (and not the body variables as is currently the case). 
 
I'm not sure I could follow you there. Could you give an example?

A few examples that come to my mind are (regarding final counter variables):

// Is it ok to update the counter variable in the check expression, instead of the increment expression?
for (final i = 0; ++i < 5;) ;

// Can the check and the increment expression both update the counter?
for (final i = 0; ++i < 5; ++i) ;

// Is it ok to assign to the counter twice in the same expression?
for (final i = 0; i < 5; i = ++i + ++i) ;

tatumizer-v0.2

unread,
Nov 13, 2017, 4:00:49 PM11/13/17
to Dart Misc
@Florian: if we put it in functional form, like range(0, 3) then it's certainly exclusive. In dart, whenever you have start and end indexes, end index is exclusive.
So the natural intuition  for anyone who dealt with 30 methods in dart, all exclusive, is to assume this is exclusive, too. 

Elsewhere, if you have double dot for "inclusive", you also have triple dot for "exclusive". For me, 3 dots look no more exclusive than 2 dots, not sure what intuition is behind this distinction 



Danny Tuppeny

unread,
Nov 13, 2017, 4:17:56 PM11/13/17
to mi...@dartlang.org
On Mon, 13 Nov 2017 at 19:27 Randal L. Schwartz <mer...@stonehenge.com> wrote:
Danny> Feels a bit weird to me having an Iterable<int>-returning method hanging
Danny> off an int though.

Any worse than String.split?

Hmmm, well at least it feels like the result of splitting a string is entirely related to the string; whereas .to(x) on the int seems like it's just using it as a start point.

That said, I don't know if my weird feeling is well founded, since I do similar things elsewhere (eg. in C# collection1.Union(collection2))!

On Mon, 13 Nov 2017 at 21:00 tatumizer-v0.2 <tatu...@gmail.com> wrote:
Elsewhere, if you have double dot for "inclusive", you also have triple dot for "exclusive". For me, 3 dots look no more exclusive than 2 dots, not sure what intuition is behind this distinction 

I agree, but I'd say have double-dot for inclusive and don't have an exclusive. Why do you want it? Is there anywhere other than arrays where it's useful? (and probably there are better ways to enumerate arrays).

tatumizer-v0.2

unread,
Nov 13, 2017, 4:43:48 PM11/13/17
to Dart Misc
@Danny: 3 dots is not the point!
The point is: dart proclaims everywhere that endIndex is exclusive. Dart is proud of his own consistency in that respect.
When I forget everything dart, and remember just 1 thing, this thing is: endIndex is exclusive.
And suddenly, if you write it as [1..3] it's inclusive. OK, you may not call it endIndex (that's the only choice if we want to preserve sanity).
Suppose we call it endRange, fine. (Good luck remembering that)

BUT: when you write it for iterating over an array, this "endRange" is intuitively an "endIndex". Confusion ensues.

tatumizer-v0.2

unread,
Nov 13, 2017, 11:50:05 PM11/13/17
to Dart Misc
in Swift:
  1. for tickMark in 0..<minutes {
  2. // render the tick mark each minute (60 times)
  3. }
B I think that's a reasonable choice.

B BTW, in swift, triple dot (as in 1...5) is inclusive. I find it more intuitive, because mnemonically, 3 dots indicate longer sequence than 2 dots.

Erik Ernst

unread,
Nov 14, 2017, 4:31:48 AM11/14/17
to Dart Misc
On Mon, Nov 13, 2017 at 6:23 PM, 'Florian Loitsch' via Dart Misc <mi...@dartlang.org> wrote:
On Mon, Nov 13, 2017 at 3:31 AM 'Erik Ernst' via Dart Misc <mi...@dartlang.org> wrote:
On Mon, Nov 13, 2017 at 12:19 PM, Erik Ernst <eer...@google.com> wrote:
On Mon, Nov 13, 2017 at 12:03 PM, Philipp S <philippsc...@gmail.com> wrote:
On Mon, Nov 13, 2017 at 8:49 AM, Man Hoang <joll...@gmail.com> wrote:
I would prioritize readability over less typing. A range operator would be a nice addition.

Also, implicit variable declaration is scary, at least for me. I prefer having all variables explicitly declared.

Note that no variables are declared implicitly, even though the syntactic marker is small: The `:` in `:=` makes it explicit that this is a declaration.

There is something else I find really confusing and off-putting about the proposed `final` in for loops. Lets call the expressions in a for statement "initializer", "check" and "increment". With the proposed change, you have this weird situation where the `final` modifier of a loop variable `i` is scoped to the loop body, but is invisible to the loop expressions. check and increment can *write* to a final variable! That feels ... wrong, inconsistent.

The appropriate intuition here is that each step of the iteration gets its own copy of the iteration variable (so `i` during the first iteration is simply a different variable than `i` during the second iteration). So increment doesn't write to the same final variable at the beginning of each step, it initializes a fresh final variable.

This is required in order to make function closures that capture the iteration variable see the value of the iteration where the capture occurred (even if it is called after the entire loop has terminated). This is the reason why so many examples use function literals (like () => i) to show how the iteration variable works. If a single variable had been used for the entire loop execution then all those closures would have returned the latest value that this variable had, so the printout would have been `2 2 2` rather than `0 1 2`.

This is not an accident: The language specification section 17.6 explains the semantics in terms of a fresh variable per iteration, exactly because of the confusion which is traditionally caused by captured iteration variables that end up having "the latest value" when such a closure is invoked.

Actually, the language specification does not spell out how an expression like `i++` must be considered to mean `ik+1 = ik + 1`. We will fix that.

That's because `i++` only works on one instance of the variable.
The syntax expansion of a for-loop never splits the visibility of a variable inside an update clause.

I just noted that the current specification, avoiding such a split into two variables in the update clause, will not work when the loop variable is final. In the concrete example it would make an attempt to evaluate `ik+1++` (17.6.1 says evaluate [v''/v]e, where v'' is ik+1, v is i, and e is i++). So we will need to say it in a slightly different way.

Matthew Butler

unread,
Nov 14, 2017, 12:48:32 PM11/14/17
to Dart Misc


On Friday, November 10, 2017 at 5:30:36 PM UTC-4, tatumizer-v0.2 wrote:
Would it be prudent to make another step in the same direction so that we can write
for (i := 0; i < 3; i++)
simply as
for (i :<3)
(colon followed by <)
The point is that 90+% of all loops are like this - why do we need an extra noise, which only invites  bugs?


My issue with this proposal is that it's extremely limited. 
1) I can't decrement?
2) I can't start with a different offset? (eg 1..3 is at least clear it starts at 1 even if it's arguable if it ends at 2 or 3.)

Concern 1 also applies to the range proposal though as mentioned at least it's clear that you can start from say 5 and increment to 9. Maybe range could also support a reverse direction (5..1) which would address point 1 as well.

Both however also run into:
3) Can only increment an individual amount. Eg: I can't do
for (i := 0; i < 10; i += 2) {...}

 That said, the range option is a "for in" which acts on an iterator, whereas I'm assuming your proposal is to replace the for loop syntax based on your example. I don't see a benefit in shortcutting that limited subset of usage for the for loop (since it's not specifically acting on an iterator)

Danny Tuppeny

unread,
Nov 14, 2017, 12:54:51 PM11/14/17
to mi...@dartlang.org
On Mon, 13 Nov 2017 at 21:43 tatumizer-v0.2 <tatu...@gmail.com> wrote:
@Danny: 3 dots is not the point!
The point is: dart proclaims everywhere that endIndex is exclusive. Dart is proud of his own consistency in that respect.

Sorry, I see what you mean now. This sounds kinda crazy to me though (endIndex sounds rather like it should be the last index, and having to explicitly call out that one is inclusive and one is exclusive in the docs suggests it's not obvious)... v2 is the time to fix (break?) stuff like this, right? ;-)

Tbh, given this knowledge I think having a range operator might not be such a great idea - having only an inclusive one would be confusing because of the reasons above and having two would also be confusing (it's some extra knowledge you need to know just to read the code correctly, and also may end up being different to other languages with the same operator).

=/

Lee Crocker

unread,
Nov 14, 2017, 1:14:51 PM11/14/17
to mi...@dartlang.org
Isn't this a fairly well-settled issue in CS? Array indices that are 0-based and end bounds that are exclusive result in a lot fewer stray "+1"s littering your code. The fact that ordinary humans think of 3 things as "1, 2, 3" is just a bad habit that we should break early. Maybe in a few thousand years culture will evolve to use little-endian hexadecimal too, but I'm not holding my breath for that one. Or maybe I am just biased by years of Python.

--

Danny Tuppeny

unread,
Nov 14, 2017, 4:14:05 PM11/14/17
to mi...@dartlang.org
On Tue, 14 Nov 2017 at 18:14 Lee Crocker <l...@ansync.com> wrote:
Isn't this a fairly well-settled issue in CS? Array indices that are 0-based and end bounds that are exclusive result in a lot fewer stray "+1"s littering your code.

But how far should we go applying this to thinks that aren't indexes? What is the answer to "generate the numbers from 1 to 10"? In English it would include 10.

That aside, the status quo also isn't perfect - off-by-one/range errors are still pretty common and people often get < and <= mixed up in conditions. It's also pretty common to see "array.length - 1" (I actually rather liked that VBScript had a UBound() function!). I don't know how accurate the results are, but searching for 'off by one' on GitHub gives 15 million commits! ;(

I think "exclusive end index" feels ok partly because we're used to it and partly because in many cases it's the same as length/count - to a non-programmer I think it would seem rather confusing..

If you want items 2,3,4 from the array you pass 2 as the start index but 5 as the end index.

Sounds kinda wonky for not a great reason. Sure, you could argue that non-programmers aren't programming, but we should really be working to reduce the amount of knowledge needed to get started (or at least, the potential for people to get confused/do things wrong - especially things that blow up at runtime).

I'm not sure we'll ever get away from exclusive-end-index but I do think in many cases it could've been avoided (eg. maybe startIndex and length would work in many cases and eliminate the potential for confusion - I'm aware I said something was silly in C# yesterday, but now I'm thinking it makes sense!). I also think we should be very careful extending the idea to things that just aren't indexes.

tatumizer-v0.2

unread,
Nov 14, 2017, 4:33:21 PM11/14/17
to Dart Misc
 I don't see a benefit in shortcutting that limited subset of usage

This "limited subset" accounts for 90+% of use cases. In any natural language, any phrase occurring with such frequency would be very quickly abbreviated. That's why use see expressions like 'BTW', "WRT', 'LOL' so often.
And swift realizes it, too. It provides a general construct for iteration up or down and with step N, but this general case is intentionally made verbose. However, for the case of (int i=0; i<n; i++) it provides a more concise form 0..<n

Man Hoang

unread,
Nov 14, 2017, 9:24:11 PM11/14/17
to mi...@dartlang.org
We can go further by introducing a new for loop to solve the 90+% case.

forn (i, n) {
 
print(i);
}

// Equivalent to
for (final i = 0; i < n; i++) {
 
print(i);
}

Another proposal is adding const class.

// Implicit default const constructor.
const class Injectable {}

// Only a const class can extend another const class.
const class Directive extends Injectable {
 
// Fields are final, no need to include the `final` keyword.
 
String selector;

 
// Constructors are const, no need for the `const` keyword.
 
Directive({this.selector});
}


I actually fired an issue here.

tatumizer-v0.2

unread,
Nov 15, 2017, 12:35:40 AM11/15/17
to Dart Misc
forn (i, n) {
  
print(i);
}

+1. Simplicity is the mother of beauty :)

For "const"- we need a different keyword for immutable classes. Const is reserved only for the stuff known at compile time (AFAIK), and this limitation is much more severe than we need for everything "final".  

Danny Tuppeny

unread,
Nov 15, 2017, 8:07:52 AM11/15/17
to mi...@dartlang.org
On Wed, 15 Nov 2017 at 02:24 Man Hoang <joll...@gmail.com> wrote:
We can go further by introducing a new for loop to solve the 90+% case.

forn (i, n) {
 
print(i);
}

// Equivalent to
for (final i = 0; i < n; i++) {
 
print(i);
}

This doesn't resolve the ambiguity though... In the for loop it's clear whether n is included or not, but in the forn loop it's not (also, you can't control the starting number and it seems weird that the i is a decleration when it looks very similar to the n).

Out of curiosity, I just quizzed a bunch of my colleagues (all pretty experienced devs). I gave them absolutely no context and just asked them what they expect "for (i in 1..5)" to do. Only two people didn't say "1,2,3,4,5" and their answers were:

- I'd expect 1,2,3,4 but only because this seems like a trick question
- 1,2,3,4,5 but I was caught out recently expecting this operator in F# to work like C#'s Enumerable.Range having the second argument be the count (he said although it surprised him, it kinda obvious when he realised what it did)

It's a very small sample (6) but does suggests that an exclusive range is not a commonly expected result. I don't think introducing a new way of doing loops that doesn't make it clear whether it's inclusive/exclusive at the upper bound would be a great idea (depending on whether you're looping an array or outputting numbers, it would end up wonky with a +1/-1). I think it's blurring an already confusing concept (array indexes) with something that doesn't need to be confusing (a sequence of numbers from x to y).

tatumizer-v0.2

unread,
Nov 15, 2017, 9:22:58 AM11/15/17
to mi...@dartlang.org
@Danny: I think there's a big difference between
for (i in 1..n) 
and 
forn (i, n)

The reason for the former to be perceived as inclusive is that the notation like '1,...n'  is standard in math, and there, it's inclusive. 

You have to repeat your (unscientific) poll :)

In dart, all endIndexes are exclusive (as they should be). As soon as just a single inclusive pattern is introduced - it will ruin the whole show IMO.



Philipp S

unread,
Nov 15, 2017, 10:21:49 AM11/15/17
to Dart Misc
the notation like '1,...n'  is standard in math, and there, it's inclusive. 
 I'm currently studying at a german university, and I've never seen that notation. When I want to specify an integer range, I'd write: ∀i∈[0, n)∩ℕ₀, where [0, n) is the range of real numbers from 0 (inclusive) to n (exclusive), and the intersection with ℕ₀ (non-negative integers) yields the set {0, 1, 2, ..., n-2, n-1}. Is that notation also taught/used wherever you come from?

Also, while arguing about a range operator, do you keep in mind that `..` is already in use for the cascade operator? How about a `to` operator instead, possibly in conjunction with the range brackets, like so: `[0 to n], [0 to n), (0 to n], (0 to n)`? One would have to come up with a way to specify the stepsize, though – or something else that represents the `∩ℕ₀`. :-)

Matthew Butler

unread,
Nov 15, 2017, 11:11:56 AM11/15/17
to Dart Misc


On Tuesday, November 14, 2017 at 5:33:21 PM UTC-4, tatumizer-v0.2 wrote:
 I don't see a benefit in shortcutting that limited subset of usage

This "limited subset" accounts for 90+% of use cases.
Based on what sample source is this? Looking through a number of projects and the dart sdk itself, it appears that 75% of usages are for in clauses. While the remaining clauses are roughly 80% of for (i = 0; i < value; i++) your statement doesn't hold true in the least.
 
In any natural language, any phrase occurring with such frequency would be very quickly abbreviated. That's why use see expressions like 'BTW', "WRT', 'LOL' so often.
if this were the rule, then how is it the only usage in your text is to cite it as an example which isn't use?
And swift realizes it, too. It provides a general construct for iteration up or down and with step N, but this general case is intentionally made verbose. However, for the case of (int i=0; i<n; i++) it provides a more concise form 0..<n

So you're saying swift provides a range operator (not a for loop construct, since it's usable outside of for loops as well) Which itself is more usable than your proposal (for instance I don't need to start at 0). 

tatumizer-v0.2

unread,
Nov 15, 2017, 12:28:05 PM11/15/17
to Dart Misc
> Based on what sample source is this?
But the discussion is not about  "for ... in" ... - it's about for (i=v0; i<v1; i++), right?  I admit I took 90% from the thin air, but your 80% don't refute that (in statistics, everything is a bit fuzzy).

As an aside: I personally use 'for in' only for the stuff that is not performance-critical.  In libraries, especially in low-level libraries, my instinct is to avoid 'for in' because I'm not sure how well it would perform. (I suspect it would suck, but I might be wrong).
To get good performance, compiler has to optimize 'for in' for arrays specifically (as opposed to generic Iterable) - I have no idea if it's done or not, and for what kinds of arrays. (Again, please correct me if I'm wrong)

Also, you often need both element and index. And if performance is not an issue, then why not use just functional form like "map" or forEach or what-not? Then you don't even need 'for in'! But all this is beside the point, b/c we are discussing traditional "for".

To find out the proportion exactly, there's no choice but writing a script that runs through large collection of dart code. Then we will know for sure whether it's 80% or 90% or something else, but my bet still remains :).

tatumizer-v0.2

unread,
Nov 15, 2017, 6:11:05 PM11/15/17
to Dart Misc
, I'd write: ∀i∈[0, n)∩ℕ₀
> Is that notation also taught/used wherever you come from? 
It might depend not so much on the country, but on specific tastes of the author of the textbook. I find the above notation funny. Why real numbers and open intervals should get involved here? If my memory doesn't fail me, in Russian textbooks of the time (40 years ago), they would simply say  ∀i, 0 <= i < n or maybe ∀i∈ℕ, 0 <= i < n (notice comma, which stands for "such that")
(<= is one symbol, of course)
There was another notation like i ∈ { 0, 1, ... n-1 } - I think it was more common. And that's the notation I meant. Maybe the times have changed though.


 

Istvan Soos

unread,
Nov 16, 2017, 1:56:15 AM11/16/17
to General Dart Discussion
> for (i :<3) [... vs ...] for (int i = 0; i < 3; i++)

Anecdotal evidence: I like the "for (int i = 0; i < n; i++) {}" much
better than any of the suggestions in this thread. *Explicit* in every
part, leaves no room to guesswork, and unless you have 20+ character
variables for i and n, it will fit in a single line, easily readable.

I have spent probably more time reading this thread than the time I
could have spared by not typing the additional 3-8 characters in each
of my for loops ever, no matter how clever the abbreviation could have
been. That is the cost of not being explicit: you need to interpret
it, and people from different background may interpret it differently
(and argue about it). For me, that is a good indicator that even if it
were to hit the language, I wouldn't use it, because others reading
the code will get harder time to interpret it.

Cheers,
Istvan

tatumizer-v0.2

unread,
Nov 16, 2017, 7:20:58 AM11/16/17
to Dart Misc
*Explicit* in every 
part, leaves no room to guesswork
And a lot of room for fun debugging your nested loops where you forget to rename i to j in i++ 
There;s a good summary of disadvantages here:
I like this argument (re-phrasing a bit)
If the for-loop did not exist, and was just submitted for discussion today, it would most likely be rejected as a bad idea. (in dart, I'm 100% sure about it)

I have personal grudge against for loops (on several occasions, forgot to rename i to j), so can't be objective :)
 


Istvan Soos

unread,
Nov 16, 2017, 11:21:46 AM11/16/17
to General Dart Discussion
> And a lot of room for fun debugging your nested loops where you forget to rename i to j in i++

I'd classify this issue in the linter-rule category: if the
middle-condition or the increment references a variable from another
for loop, but none from the current one, make it a warning. It would
still allow "for (int j = 0; j < i; j++)", but would warn about "for
(int j = 0; i < length; j++)" or "for (int j = 0; j < i; i++)".

> If the for-loop did not exist, and was just submitted for discussion today, it would most likely be rejected as a bad idea.

I'd be curious if there is a serious and modern programming language
that *doesn't have* "for (int i = 0; i < n; i++)" style loop, *and*
has something else that serves similar purpose. My guess is the
traditional for loop survives across languages because it solves a
problem reasonable well.

Cheers,
Istvan

tatumizer-v0.2

unread,
Nov 16, 2017, 11:31:02 AM11/16/17
to Dart Misc
I'd be curious if there is a serious and modern programming language 
that *doesn't have* "for (int i = 0; i < n; i++)" style loop, *and* 
has something else that serves similar purpose.

Swift? Kotlin? I suspect every modern language got rid of it, no? 

tatumizer-v0.2

unread,
Nov 16, 2017, 6:16:44 PM11/16/17
to Dart Misc
> Swift? Kotlin? I suspect every modern language got rid of it
Adding to the list: 
nim
Julia
Rust
R
Actually, every language I looked into. The only exception is go, but it stands out on many fronts :)

.

Filipe Morgado

unread,
Nov 17, 2017, 8:04:03 AM11/17/17
to Dart Misc
"For loops", "while", even "if", etc ... could be obsolete and replaced with iterators and closures.

Range(0, 10).forEach((int i) {
 
... do something with 'i' ...
});

With range syntax and shorter 'forEach' (like C#):
0..10.each((/*int is inferred*/ i) {
 
... do something with 'i' ...
});
"0..10", "0...10", "0..<10", "10..>=0" ... whatever ... as long as it's expressive.

And with special syntax for methods which take a closure as single argument:
0..10.each |i| {
 
... so something with 'i' ...
}


With good editor support, we'd just write "0..10.ea"<ENTER> and it would insert the block above.
It's quite faster than writing "for(int i = 0; i < 10; i++) {}" by hand.

It would work well for maps:
final myMap = new Map<String, String>();
myMap
.each |k, v| {  // String k, String v is inferred
 
... do something with 'k', 'v' ...
}
// Or ...
myMap
.each |k, v| => DoSomething(k, v);

It's "different" (compared to old mainstream languages) and would hinder the Dart's familiarity goal, but I think it would be a nice improvement.

Of course, this would only work if the abstraction is zero-cost, all structures inlined and all allocations sunk.

I'd vote for this.

tatumizer-v0.2

unread,
Nov 19, 2017, 6:33:36 PM11/19/17
to Dart Misc
I think it would be interesting to add not just one, but 2 tricks:
1) support a literal [x..<y] (e.g. [0..<3] which returns an Iterable<int>, same as [0, 1, 2]
2) for Iterable<int>, support operators *int and +int, so we can write [0..<3]*2+1 - which is equivalent to [1, 3, 5]
Then we can write
for (i in [0..<3]*2+1) {...}
and
for (i in [0..<3]*-2-1) // descending sequence

Cute? Stupid? All of the above? :)
Not sure there's a language that provides this feature. I think it will become an instant hit!
(In principle, we can multiply Iterables<int> - in the sense of Cartesian product. But here the things become a bit complicated :)




Reply all
Reply to author
Forward
0 new messages