Welcome to the Dart Language and Library Newsletter.
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); }
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 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 Completer
class.
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).
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.
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:
new Iterable.empty()
should just directly create an _EmptyIterable
. (Our compilers inline these simple constructors, so this is not a real problem in practice).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); }
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 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 }
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.
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.
--
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.
I mean the for..in loop.
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 (i := 0; i < 10; i + 1) print(i);
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);
--
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.
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 - Google Danmark ApSSkt Petri Passage 5, 2 sal, 1165 København K, DenmarkCVR no. 28866984
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.
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.
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. }
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 :-)
--
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.
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.
Range in current form, as infor (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 tofor (i:<3)The meaning is absolutely clear, and can't be mistaken for anything else
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)..
>>>>> "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.
--
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
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.
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).
// 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) ;
Danny> Feels a bit weird to me having an Iterable<int>-returning method hanging
Danny> off an int though.
Any worse than String.split?
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
for tickMark in 0..<minutes {
// render the tick mark each minute (60 times)
}
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.
Would it be prudent to make another step in the same direction so that we can writefor (i := 0; i < 3; i++)simply asfor (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?
for (i := 0; i < 10; i += 2) {...}
@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.
--
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.
I don't see a benefit in shortcutting that limited subset of usage
forn (i, n) {
print(i);
}
// Equivalent to
for (final i = 0; i < n; i++) {
print(i);
}
// 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});
}
forn (i, n) {
print(i);
}
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);
}
the notation like '1,...n' is standard in math, and there, it's inclusive.
I don't see a benefit in shortcutting that limited subset of usageThis "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
, I'd write: ∀i∈[0, n)∩ℕ₀
*Explicit* in every
part, leaves no room to guesswork
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.
Range(0, 10).forEach((int i) {
... do something with 'i' ...
});
0..10.each((/*int is inferred*/ i) {
... do something with 'i' ...
});
0..10.each |i| {
... so something with 'i' ...
}
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);