Coder Social home page Coder Social logo

language's Introduction

Dart language evolution

Build Status

This repository is a place for the Dart language team to work on language changes and features, and to solicit and accept feedback and requests.

Dart language team

As of January 2024, the Dart language team consists of:

Organization

We follow this process for planning and rolling out language changes.

Features currently being worked on are listed in the language funnel.

Contributing

Anyone can participate in the discussion about language changes by participating on the dart language mailing list, by replying to issues in this repository, and by uploading documents, tests or other resources.

When commenting on issues in this repository, keep in mind:

  • 👍 reactions are more useful than comments to show support.
  • Motivating examples help us understand why you want new features more than pointers to other languages which have them. We love hearing feedback about your experiences with other languages, but we also want to know why they are right for Dart in particular.

License & patents

See LICENSE and PATENTS.

language's People

Contributors

alexgoussev avatar bwilkerson avatar creativecreatorormaybenot avatar davidmorgan avatar dcharkes avatar dependabot[bot] avatar devoncarew avatar eernstg avatar franklinyow avatar gnprice avatar jakemac53 avatar jmewes avatar kevmoo avatar kwalrath avatar leafpetersen avatar lrhn avatar mit-mit avatar mnordine avatar mraleph avatar munificent avatar natebosch avatar parlough avatar pq avatar scheglov avatar sgrekhov avatar sigurdm avatar srawlins avatar stereotype441 avatar vsmenon avatar yumatan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

language's Issues

Feature request: support for nested classes.

This issue was originally filed by [email protected]


It would be nice if we can introduce classes local to a given enclosing class.

class Outer {
  void doSomething() {
    var inner = new Inner();
  }
  class _Inner {
       
  }
}

This allows define class that is invisible outside of the Outer class scope.

I understand that this feature requires more investigation. As for me it has a limited power if a class is not the first class object (or a class could be a class member like fields or methods).

Add an expression-local declaration (let-expression).

There is no way to locally declare a variable in an expression.
In statements, a single statement can be replaced by a block statement that contains local variable declarations, but there is no way to do the same thing in an expression.

Example code (using "let/in" as syntax):

let var tmp = new Something() in new Other(tmp, tmp)

This can be useful in initializer lists where you can need to create a new object and use it in more than one place.

The current alternative, as used by the specification to explain desugaring of some constructs, uses a function literal to bind the new variable, like ((tmp) => new Other (tmp, tmp))(new Something()).
This has the disadvantage of not working well with async operations (if new Other(..) was more complex and contained an await, it would not be a correct rewrite).

The new syntax could be used by the specification to define desugarings, and can be used to make some expressions more readable by reducing the scope of temporary variables.

Alternatively, now that we have definite assignment analysis, we could allow var x = e as an expression.
It would effectively introduce x into the current scope, but the variable cannot be accessed until after the declaration point, even if that is in the middle of an expression. It'll b definitely unassigned before and definitely assigned after, which is something we can already handle.

Then the above example becomes Other(var tmp = Something(), tmp).
It will probably have to use the var or final marker, using a type can potentially be hard to parse: f(x + int y = 2, 2).
The precedence of a declaration would be very low, so the only place it won't need to be parenthesized is if it's at the end of an expression, or it's a collection element or parameter list entry (comma delimited).

Optional auto-generation of operator== and hashCode on classes with const constructors

It is a common Dart idiom to have a class with all-final fields, a const constructor, a toString, an operator==, and a hashCode function.

The latter two are boilerplate:

  @override
  bool operator ==(dynamic other) {
    if (other.runtimeType != runtimeType)
      return false;
    final T typedOther = other;
    return typedOther.field1 == field1
        && typedOther.field2 == field2
        && typedOther.fieldN == fieldN;
  }

  @override
  int get hashCode => hashValues(field1, field2, fieldN);

It would be nice if there was some syntax or shorthand we could provide that would automatically provide these.

cc @abarth

`async*` methods should start synchronously when their streams are listened to.

The async functions start synchronously in Dart 2. So should the async* functions, meaning the initial part of the function should be called immediately when a client listens on the returned stream.
We have not implemented that yet.

The specification says that

When s is listened to, execution of the body of f will begin.
(where s is the stream returned by calling the async* function f).

Starting immediately is important. It allows an async* function to immediately listen to, say, a broadcast stream immediately when the user asks it to start. Otherwise it might miss events fired between the time of the request and when the async* function actually starts.

Example:

class Stream<T> {
   ...
   Stream<T> takeWhile(bool test(T value)) async* {
     await for (var value in this) {
       if (!test(value)) break;
       yield value;
     }
   }
  ...
}

This implementation of takeWhile is currently not viable because the async* function doesn't start immediately.

(You still can't send events synchronously during the onListen callback, so this would not allow you to deliver events before the subscription has been returned by the listen call).

Union types

Currently Dart does not support function overloads based on parameter type and this makes it verbose/awkward to clone/wrap Javascript APIs which accept multiple types.

For example, WebSocket.send() can accept a String, a Blob or an ArrayBuffer.

In Dart, this can either be exposed as:
WebSocket.sendString, sendBlob, sendArrayBuffer which is verbose
or:
WebSocket.send(Dynamic data), which is no longer a self-documenting API.

It would be great to have some variant of union types, along the lines of JSDoc:
WebSocket.send(String|Blob|ArrayBuffer data);

Constant constructor call should be legal in another constant constructor definition.

This issue was originally filed by @Cat-sushi


The code below is illegal with the current language specification.

  class C {
    final x;
    const C(this.x);
  }

  class D {
    final C c;
    const D(x) : this.c = const C(x); // compile-time error!
  }

I understand that 'x' in 'const C(x);' is not a compile-time constant, but a potentially constant expression, and this is the reason why the constructor call 'call C(x);' is illegal.
I also understand the reason why the 'x' is potentially constant, the constantivity of 'x' is depends on the call site which calls the constructor whether with 'const' or with 'new'.

On the other hand, with my understanding, the compiler evaluates the result of constant expressions at compile-time, and the compiler can check constantivity at compile-time with call site sequence.
At the same time, in run-time context, constructors are less constrained, and the language processor can simply regard 'const' of 'const C(x);' as 'new', and can call the prepared run-time constructor.
I think the disregard of keyword 'const' and the need for the run-time constructor of the constructor call 'const C(x);' in run-time context are kind of similar to those of constant constructor definition like 'const C(this.x);' or const 'D(x)...;'.

So, regardless of discussion in issue dart-lang/sdk#392 and issue dart-lang/sdk#19558, I think constant constructor calls in another constant constructor definition should be legal.
And, I feel it is more natural.

In addition, I think this proposal is upper compatible of the current language specification.

Don't allow cascades on expressions with infix operators.

The grammar currently contains the production expression : conditionalExpression cascadeSection*
This allows such atrocities as:

test ? foo : bar..baz()  // meaning (test ? foo : bar)..baz()
e1 ?? e2..baz() // meaning (e1 ?? e2)..baz()

etc, all down the expression hierarchy to binary multiplicative and unary prefix operators

a * b..bar() // meaning (a * b)..bar()
await foo()..bar()  // meaning (await foo())..bar()
~foo..bar()  // meaning (~foo)..bar())

Only the prefix operators look reasonable here, but it might still be confusing because it differs in behavior from ~foo.bar(). Arguably that's a common issue in itself when -2.toString() fails to compile.)

We should not allow cascades direction on expressions which contain infix or prefix operators. The code reads completely contrary to how it actually works.
I suggest changing the grammar from:

expression
    :    (formalParameterPart functionExpressionBodyPrefix) =>
         functionExpression
    |    throwExpression
    |    (assignableExpression assignmentOperator) =>
         assignableExpression assignmentOperator expression
    |    conditionalExpression cascadeSection*
    ;

to

expression
    :    (formalParameterPart functionExpressionBodyPrefix) =>
         functionExpression
    |    throwExpression
    |    (assignableExpression assignmentOperator) =>
         assignableExpression assignmentOperator expression
    |    cascadeExpression
    ;
cascadeExpression
    :    prefixExpression cascadeSection+
    |    conditionalExpression 

(and change all other occurrences of conditionalExpression cascadeSection* to cascadeExpression too).

(Edit: Changed from postfixExpression cascadeSection+ to prefixExpression cascadeSection+ to allow, e.g., -x..foo()).

Extensible pattern-matching

This issue was originally filed by [email protected]


It would be nice to match a value against patterns with a terse syntax.

This is similar to 'switch' (and could possibly share the same syntax) but instead of exact-match, the semantics would be determined by the pattern used.

When the pattern is a RegExp, it would match strings that conform to it.
A Type would match instances of that type.
User classes should be able to implement their own pattern behaviour.
Any value without special pattern behaviour would match using ==

for example:

switch (value) {
  match Exception: throw value;
  match const RegExp('\s+') return 'whitespace';
  match 42: return 'the answer';
  default: return 'unrecognized';
}

There was some discussion here: https://groups.google.com/a/dartlang.org/group/misc/browse_thread/thread/a3e75c24c6dd4f03/

A pattern might also be able provide values which can be bound to names (e.g. the Match object corresponding to a regexp match).

FutureOr conditional expression typing issue

I'm running into a typing issue similar to dart-lang/sdk#28783, but in the context of a conditional expression (?:)

  FutureOr<bool> routerCanDeactivate(next, prev) =>
      someExpression
          ? true
          : methodReturningFutureBool();

Under 1.22.1, this results in an analyzer warning:

WARNING: Unsafe implicit cast from 'Object' to 'FutureOr'. This usually indicates that type information was lost and resulted in 'dynamic' and/or a place that will have a failure at runtime.

But the analyzer is happy if I put an explicit cast (which IMHO should be unnecessary):

  FutureOr<bool> routerCanDeactivate(next, prev) =>
      someExpression
          ? true as FutureOr<bool> // <================== cast
          : methodReturningFutureBool();

@leafpetersen @bwilkerson @nex - I wasn't sure if you'd want a separate issue for this, or whether you'd prefer clumping it up with dart-lang/sdk#28783. It felt like ?: might have its typing rules handled separately from if-then-else statements. Anyhow, I'll let you mark this as a dup if you want to merge the issues.

cc @kwalrath @matanlurey

Add a "try-with-resources" or "using" style syntax for automatic resource management

One neat feature in Java and C# that I think would be great for Dart is the ability to have a resource be automatically closeable/disposable. This is accomplished in Java with the Closeable and/or AutoCloseable interface paired with the following try-catch syntax:

try (CloseableResource cr = new CloseableResource) {
  ...
} //optionally add a catch and/or finally branch here

and in C# with the IDisposable and using syntax:

using (IDisposableResource idr = new IDisposableResource()) {
  ...
}

This helps catch resource leaks because the compiler can detect and warn the developer when close/dispose has not been called or has been called unsafely. I'd love to see this feature in Dart!

Edits: Spelling and grammar fixes

Mixin declaration allowing super-invocations.

Solution for issue #6.

See the feature specification document.

Implementation issue: #12

Introduce a new syntax for declaring mixins:

mixin Mixin<TypeArgs> on SuperType1, SuperType2 implements I1, I2 {
  member-declarations
}

This declaration introduces a mixin, like one derived from a class, except that the mixin can only be applied to a super-class which implements SuperType1 and SuperType2. In return, it can then do super-invocations targeting members of the SuperType1 or SuperType2 interfaces.

Each time this mixin is applied to a super-class, it must check that all the members that are invoked by super-invocations actually have implementations in the super-class which satisfies the most specific requirement of SuperType1 and SuperType2 for that member.

Allow named function arguments anywhere in the argument list

Since named arguments are distinguishable from positional ones, allowing named arguments to be placed anywhere in the argument list can be done without changing calling semantics.

It is an advantage in some cases where you want to put a function argument last. Example:

expectAsync(() {
  ...
}, count: 2);

would be more readable as:

expectAsync(count: 2, () {
  ...
});

Allow parameter defaults for null values

Please save us from having to do this to capture both undefined and null values. 😃 I know undefined isn't a thing in Dart but it is treating "unspecified" as being different than "null".

void main() {
  Thing nullThing = Thing(text: null);
  print(nullThing.text); // "default"

  Thing undefinedThing = Thing();
  print(undefinedThing.text); // "default"
}


class Thing{
  String text;
  
  Thing({
    String text: "default"
  }) : this.text = text ?? "default";
}

Ideally:

class Thing{
  String text;
  
  Thing({
    this.text: "default"
  });
}

Why? Because when being passed Maps from a NoSQL database that may not have complete data, and then using it to create a new class instance, I have not found a good way to pass undefined to the class constructor arguments, when myMap['notHere'] == null

Support generic arguments for (named) constructors

Sometimes constructors need to have generics that don't appear in the class itself in order to fully express their type relationships. new Map.fromIterable() is a good example:

Map.fromIterable(Iterable elements, {K key(element), V value(element)});

If elements has a generic type, the key() and value() callbacks are guaranteed to be passed an argument of that type, but there's currently no way to express that. Using generic types here blocks inference and makes it very difficult to use this API in strong mode. I propose that we be able to declare:

Map.fromIterable<E>(Iterable<E> elements, {K key(E element), V value(E element)});

which would be called like new Map<String, String>.fromIterable<int>(...). This syntax allows the user to omit the generic argument to the constructor while retaining it for the class itself if desired.

As specified, type inference infers no constraint for Null <: Future<T>

The local inference informal specification (dart-lang/sdk#29371) says:

  • P is a subtype match for FutureOr<Q> with respect to L under constraints
    C:
    • If P is a subtype match for Future<Q> with respect to L under
      constraints C.
    • Or P is not a subtype match for Future<Q> with respect to L under
      constraints C
      • And P is a subtype match for Q with respect to L under constraints
        C

The analyzer behavior is slightly different in the case where P is a subtype match for both Future<Q> and Q. (This happens, for instance, if P is Null). In that case, if the attempt to match P against Future<Q> generates zero constraints, but the attempt to match P against Q generates constraints, then the constraints from matching P against Q are propagated.

This has a user visible effect in the analyzer. Consider the code:

void test(Future<int> f) async {
  var t = f.then((int x) {});
  (await t).foo();
}

The analyzer attempts to infer the type argument for then by matching Null <: Future<T>. This causes the type constraint Null <: T to be generated, therefore t has type Future<Null>, and (await t).foo() produces an error. If I change the analyzer to follow the informal specification, it infers a type of Future<dynamic> for t, and no error is issued. Issuing an error seems like a better behavior here.

(Note that the common front end's behavior is different still: it infers a type of dynamic for t, due to dart-lang/sdk#33044.)

@leafpetersen @jmesserly what do you think?

Support concise function literals

Motivation for concise function literals

The syntax for a function literal includes parentheses and => or parentheses and braces, such that we may specify both the formal parameters and a function body. However, given that inference will frequently obtain type annotations for the parameters from the context, the formal parameter specification often specifies the name only. This means that we may obtain a more concise syntax for function literals if we introduce some level of support for default parameter names.

This might be very convenient, e.g., for reducing xs.map((x) => x.toString()) to xs.map(#.toString()) or even xs.map(.toString()).

Note this thread on dart-language-discuss which is one of the many locations where this discussion has occurred.

Note also that an old language team issue presented several of these ideas (here), but the repository does not currently admit public access.

Just omit the parenthesis

With this, (x) => e could be abbreviated to x => e, and possibly (x) { S } to x { S }. It would only eliminate two characters, but it would generally work everywhere, and hence it might be useful to do independently of the other proposals described below.

Using # as a default parameter name

We could let # denote an implicitly declared formal parameter, such that express (x) => x.foo(x.bar) could be abbreviated as #.foo(#.bar). In general, an expression e containing some number of occurrences of # would stand for (x) => [x/#]e where x is a fresh variable name, and [x/#] is the textual substitution operation that replaces all occurrences of # by x in its argument, here: e. (There is no need to worry about variable capture because x is fresh.)

The main issue with this approach is that it is ambiguous: #.foo(#.bar) might mean (x) => x.foo((y) => y.bar) as well as (x) => x.foo(x.bar).

We could resolve the ambiguity in several ways:

  • Require that the parameter is used exactly once in the body of the function; that is, we can abbreviate (x) => x.foo(42) as #.foo(42), but (x) => x.foo(x.bar) cannot be abbreviated to #.foo(#.bar), that would instead mean (x) => x.foo((y) => y.bar).

  • Include the braces in the abbreviation, that is, we can abbreviate (x) { f(x); x.g(42); } as { f(#); #.g(42); }. This could create ambiguities with a block of statements if used as an expressionStatement, where { f(#); #.g(42); } would mean { (x) => f(x); (y) => y.g(42); }, but both of these are rather useless (we just create some function objects and discard them), so we may simply be able to make all such nonsense an error.

  • Include the arrow in the abbreviation, that is, we can abbreviate (x) => x.foo(42) as => #.foo(42) and (x) => x.foo(x.bar) to => #.foo(#.bar).

It might be possible to take this approach with various other characters in addition to #, e.g., @, %, ? were suggested in the above-mentioned dart-language-discuss discussion. Each of them would of course have different implications for the possible choices of grammar, that is, for the syntactic forms of abbreviated function literals that we can allow.

Use a designated identifier as the default parameter name

We might use a regular identifier like _ or it rather than # as the default parameter name, in which case there is no need to change the grammar. The perceived readability of the resulting code might be better or worse. It probably doesn't make much difference when it comes to the implementation effort.

But it might make the change more breaking, because there may be existing code which is then reinterpreted to have a new meaning, e.g., we already have xs.map(_.foo) somewhere, and _ is in scope such that the code works and means the same thing as xs.map((x) => _.foo(x)). In that situation, it would be highly error-prone to give it the new meaning xs.map((x) => x.foo), and it would presumably be a nightmare to try to use rules like "_.foo is desugared to (x) => x.foo if and only if _ is undefined in the current scope".

Otherwise, the ambiguities mentioned for # would apply in this case as well, and the fixes could essentially be reused.

Use the empty string as the default parameter name

This would allow us to abbreviate (x) => x.foo(42) to .foo(42), which might be unambiguous in the grammar, but there are only few expressions where this would work. For instance, we cannot abbreviate (x) => o.foo(x) to o.foo() because that already means something else, which might just as well be the intended meaning.

So this approach might look very attractive with certain examples, but it is unlikely to scale up.

Multiple default parameter names

The above-mentioned dart-language-discuss thread also had several ideas about how to enable functions receiving multiple (positional and required) parameters to be abbreviated.

For instance, $1, $2, ... could be used, or $, $$, ..., or _1, _2, ..., such that (x, y) => x + y could be abbreviated as $1 + $2.

In return for restricting each parameter to occur exactly once and in order, we could also use the same symbol for all parameters, such that (x, y) => x + y could be abbreviated as, for instance, _ + _ or $ + $.

The former may look somewhat busy, and the latter is certainly rather restrictive, but these ideas can essentially be piled on top of all the previous proposals in order to let them support the multi-argument case.

Use a designated form of identifier as the default parameter names

We could say that $foo is a default parameter name just because it starts with $, and so is $bar. If we thus reserve all identifiers of a specific form as default parameter names, then we can express the situation where different occurrences are the same or not the same parameter, and we can also communicate more clearly what each parameter is intended to mean: (x, y) => x + y could be abbreviated as $x + $y, (x) => x + x could be abbreviated as $ + $, and:

... myWidgets.map((widget) => widget.getColor).firstWhere((color) => color == Blue) ...

// could be abbreviated into:
... myWidgets.map($widget.getColor).firstWhere($color == Blue) ...

// which may be easier to read than this:
... myWidgets.map(_.getColor).firstWhere(_ == Blue) ...

Allow a `library` directive without a name

Several tools depend on the ability to add some metadata (@annotation) at a file level.

With the test package you can write

@Skip()
library my_test;

import 'package:test/test.dart';

Or

@Skip()

import 'package:test/test.dart';

The second form, is kind of a hack. The @Skip() attribute is in fact attached to the first import directive.
It causes problems with auto-formatters that re-order or remove imports. (See dart-lang/test#517)

The first form is safer but it requires to write a name for the library.
The linter recommends the name to be of the form package_name.test.path.to.file (http://dart-lang.github.io/linter/lints/package_prefixed_library_names.html).
This rule is necessary to avoid a warning because 2 libraries have the same name.
But this is tedious to write and to maintain.

I would like to write either:

@Skip()
library;

import 'package:test/test.dart';

Or that the analyzer doesn't report warnings if 2 libraries are named _. So I can write

@Skip()
library _;

import 'package:test/test.dart';

Mixin composition

This issue was originally filed by @simonpai


Currently Dart does not support direct mixin composition as mentioned in the document (http://www.dartlang.org/articles/mixins/). However if we have a way to either define M1 * M2 or just make

class X extends Object with A, B {
   ...
}
typedef Y = Object with A, B;

X, Y valid mixin candidates, it will come in very handy to build flexible class plugins.

Algebraically there is no loophole to define the composition operator, and the association will work perfectly.

Related issue:
http://www.dartbug.com/8127

Long number literals are unreadable

Dart allows integer literals up to 64-bits, which means that the following integer literals are allowed:

100000000000000000000
0x4000000000000000

Such a number literal is unreadable, and hard to write. It's practically impossible to read the number of digits, and you can only write the number by counting in your head.

Dart should improve the syntax of number literals to make such numbers more easily readable and writable.


Proposed solutions: #2 (digit separators)

Allow integer literals where a double value is expected.

Solution to #3.

Dart should allow an integer literal to denote a double value when it's used in a context which requires a double value.

To do this, the meaning of the literal will have to depend on the expected type, aka. "the context type". The context type is already known to the compiler since Dart 2 uses that type for inference. The expected type may be empty (no requirement), but that still means that the expected type isn't exactly double.

Proposal

Currently a valid integer literal, or an integer literal prefixed with a minus sign, always evaluates to instances of int. The (potentially signed) integer literal is invalid if its numerical value cannot be represented by int (plus some edge cases for unsigned 64-bit integers).

If the context type does not allow int to be assigned to it, the program fails to compile. That includes the case where the context type is double, so programs like double x = 0; are compile-time errors because of the type-invalid assignment.

The current behavior is changed to:

If e is an integer literal which is not the operand of a unary minus operator, then:

  • If the context type is double, it is a compile-time error if the numerical value of e is not precisely representable by a double. Otherwise the static type of e is double and the result of evaluating e is a double instance representing that value.
  • Otherwise (the current behavior of e, with a static type of int).

and

If e is -n and n is an integer literal, then

  • If the context type is double, it is a compile-time error if the numerical value of n is not precisley representable by a double. Otherwise the static type of e is double and the result of evaluating e is the result of calling the unary minus operator on a double instance representing the numerical value of n.
  • Otherwise (the current behavior of -n)

This applies to both decimal and hexadecimal integer literals.
We recognize -0 in a double context as evaluating to -0.0.

In all other contexts, the integer literal evaluates to an int instance like it currently does.

Making the unrepresentable integer numeral an error allows the user to keep a simple mental model: Integer literals are always exact, floating point literals may not be. This matches the integer behavior, where an invalid int value is a compile-time error.

This is a non-breaking change since the all programs that change behavior would have an integer literal in a double context, and therefore already be compile-time errors.

Examples

The following declarations are either valid (named "valid") or compile-time errors (named "bad").

double valid = 0;  // 0.0
double valid = -0;  // -0.0
double valid = 0xFFFFFFFFFFFFF0000000000;  // Too big to be an int, valid as a double.
double bad = 0xFFFFFFFFFFFFFF;  // Valid int, invalid double, more than 53 significant bits.
double valid = 9007199254740991;  // 2^53-1.
double valid = 9007199254740992;  // 2^53. All integers up to here are representable.
double bad = 9007199254740993;  // 2^53+1, first non-representable integer.
double valid = 0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;  // Max finite double, largest allowed literal.
double bad = 0xfffffffffffff8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;  // Too large (one more 0 than above, would be Infinity if double).

Potential issues

This change is mostly simple and non-breaking, but there are potential issues.

Static typing

The change relies on static typing to decide the meaning of literals. We do that in a few other cases (instantiated tear-offs of generic methods), which can seem a bit magical, and means that the meaning of the expression depends on the type, and potentially on type inference.

Confusion because it doesn't work everywhere

This feature allows the use of integer literals as double values in some places. However, it is very restrictive in where that happens, and it's only applied to literals. That might be confusing to users who will expect integers to be usable as doubles in other situations, and it means that some refactorings are no longer valid. If they move a literal out of the double context, it might change meaning. Or, in other words, refactoring should type any variable that it introduces when extracting an expression, but that should generally be the case in Dart 2, to preserve the inference context.

Examples where context isn't expecting double:

var doubleList = [3.14159, 2.71828, 1.61803, 0]; // Obviously meant 0.0!
const x = -0;  // Not -0.0, even though there is *no other reason* to write it.
var x1 = funtionExpectingDouble(0);  // Works. 
// Let's refactor the constant into a named variable.
const zero = 0;
var x2 = funtionExpectingDouble(zero);  // Fails now?

The doubleList needs to be written as either:

var doubleList = <double>[3.14159, 2.71828, 1.61803, 0];
// or 
List<double> doubleList = [3.14159, 2.71828, 1.61803, 0];

We can't easily change this, since the num-list is existing valid code and might be deliberate.
This may be a usability pitfall to users, and we may be introducing new stumbling points to replace the ones we are fixing.

Exact integers are hard to write

We only allow integer literals that can be represented exactly by a double.
However, doubles are not always thought of as representing a single number. In some cases they are treated as if they represent a range, and any number in that range is represented by the same double value.
That affects double.toString, so there are double value where the non fractional digits of their toString will not be a valid integer-literal-as-double. Since JavaScript does not add a final .0 when printing integer valued doubles, taking a double value in JavaScript and pasting its string representation into Dart may not be a valid double-typed integer literal.

Example, in JavaScript console:

> 123456 * 7891011121314
< 974192668992941200

Now enter that into Dart:

double x = 974192668992941200;  // Compile-time error

This is a compile-time error because the value, as written, is not exactly representable as a double. The actual value of the JavaScript double is 974192668992941184.0, but the double-to-string conversion picks a representation with more trailing zeros, one that is still closer to the correct value than to any other valid double value.

This will likely be annoying.

We can, without issues, allow inexact literals, but it breaks the user expectation that an integer literal represents an exact number, and that any integer which is accepted on all platforms will meant the same thing everywhere. We already allow inexact values for double literals, but the trailing .0 is a very clear hint to the reader that we are in double land.
I recommend starting out with the strict rejection of any integer literal in a double context with a value that cannot be represented exactly by a double. We can then remove that restriction if experience tells us that it is too cumbersome to work with, but we cannot introduce a restriction after launching without it.

Implementation

Implementation is likely front-end only. Back-ends should just see a double literal.
The parser needs to allow more integer literals, so that finding the meaning of the integer literal can be delayed until the context type is known (past type inference).

Tools that process source code might need to be aware that this new combination is allowed.

JavaScript compilation

When compiling to JavaScript, integer literals will need to accept all valid non-fractional finite double values anyway, because that's what the int type can contain. They don't currently, we restrict to 64-bit values early, but we plan to change that. When that happens, the integer literals in a double context and the integer literals in a non-double context will behave exactly the same when compiled to JavaScript. That means that the code path must exist anyway, even without this feature, so adding this feature is unlikely to require large computations.

Related work

Go lang constants.
The Go language numeric constants are "bignums" with at least 256 bits of precision, and all constant computations are performed at this large precision. They are only converted to the actual int and double types when the value is used in a dynamic computation. This differs from Dart where compile-time constant computations always use the same semantics as the run-time computations.

Annoying to have to write double literals for integer values.

In Dart, unlike many other languages, integers and doubles are objects, not "primitive values". There are no special rules converting between the types on assignment (assignment generally preserves the identity of objects).

This can be annoying when you want to write something simple, like:

double x = 10;
foo(double x) => ...;
foo(42);

Here the integer number isn't allowed where a double is expected, so the program fails to compile. You have to write it as 10.0 and 42.0 to get the program compiling. This is annoying, and it feels like needless overhead when the meaning is clear.

I would be nice if you didn't have to write the extra .0, the compiler should be able to figure that out.

Proposed solutions: #4

Constant function literals

Static functions are compile time constants, but function literals are not.
It would be great if it was possible to write compile time constant function literals. They would necessarily have static scope (no access to "this") even if written inside a class declaration.

Constant function literals would create only one function object per function expression (no unification of different function expressions, even if they happen to have the same body).

This is particularly useful for default function parameters.

I suggest const <functionLiteral> as syntax. That would allow you to write, e.g.,:

class Map<K,V> {
  V getIfPresent(K key, [V onNotPresent() = const () => null]);
}

The current alternative is to declare the function as static:

class Map<K,V> {
  V getIfPresent(K key, [V onNotPresent() = _returnNull]);
  static _returnNull() => null;
}

You can use it to ensure that you don't create too many closures if you want to create a function inside a loop:

for (var x in xs) {
  x.somethingOrError(const (x) => throw new Failure(x));
}

which would otherwise create a new closure per iteration of the loop. The alternative is again to define a static function somewhere and use that, but it moves the logic away from where it's most readable.

The syntax shouldn't collide with any current use of "const", since we currently don't allow that syntax in expression position. It does collide with a constant constructor declaration, but the uses are different enough that I don't think it's a problem. If we want parity, we could allow const foo() => xx as a top-level declaration too, meaning the same as static, but that would make static redundant.

Returning a function expression from an async function causes type inference problems.

In the following code:

typedef int IntToInt(int i);
Future<IntToInt> b() async {
  return (x) => x;
}

I would expect the type of x to be inferred as int. However, it is not. It is inferred as type dynamic.

Similar constructions using synchronous code or generators work fine, e.g.:

IntToInt a() {
  return (x) => x; // x has type int
}
Iterable<IntToInt> c() sync* {
  yield (x) => x; // x has type int
}
Stream<IntToInt> d() async* {
  yield (x) => x; // x has type int
}

The problem is occurring because the context of the return expression is (correctly) inferred to be FutureOr<IntToInt>. However, when we try to infer the type of (x) => x in this context, we see that FutureOr<IntToInt> is not a function type, so we (erroneously) ignore it.

This bug currently exists only in analyzer, however I expect it will soon be replicated in front_end, so I'm filing it against both.

Consider changing the language to reduce problems with "break" at the end of switch cases.

(Split off from dart-lang/sdk#26673).

Some users have complained that "break" is required at the end of a switch case, wishing for this to be the default behavior (rather than the current behavior, which is to generate a runtime error unless the switch case in question is the last case in the switch).

Other users have complained that "break" is required at the end of a switch case where the analyzer can prove that the "break" is unreachable.

Also, it appears that analyzer doesn't actually require a "break" at the end of the last case in a switch; however, a strict reading of the language spec seems to say that it should. "Fixing" the analyzer to follow the spec in this case would likely create a lot of nuisance warnings, forcing users to introduce a lot of do-nothing "break" statements.

It seems like this area of the spec might be worth revisiting.

Mixins with super-calls.

It's sometimes useful to have a mixin which can do super-invocations.
This was allowed in Dart 1. That implementation is still being used by Flutter code because they are using the feature. We should add the feature to the language again, so we can move the Flutter code off the old implementation.

The reason the Dart 1 specification of "super mixins" was removed again was that it didn't work well in a more strongly typed language (it made all super-invocations effectively dynamic invocations) and it was never implemented by dart2js. It also did not meet the use-cases that were actually being addressed, for example it was only possible to give one super-type.

The alternative we introduce should address the current need, and we should introduce a syntax which allows more mixin features to be added in the future. For that reason it must not use a class declaration like in Dart 1. It was impossible to see whether a class declaration was intended to be a class, or just intended to be a mixin. We have reserved the mixin identifier as a built-in identifier in Dart 2.

Solutions: #7.

Support method/function overloads

This has been discussed periodically both in the issue tracker and in person, but I don't think there's a tracking issue yet. (EDIT: Here is the original issue from 2011 - dart-lang/sdk#49).

Now that we're moving to a sound type system, we have the ability to overload methods—that is, to choose which of a set of methods is called based on which arguments are passed and what their types are. This is particularly useful when the type signature of a method varies based on which arguments are or are not supplied.

Allow both optional positional and optional named arguments in the same function signature.

Dart functions currently can have only either optional named parameters or optional positional parameters, but not both.

While developing libraries repeatedly run into cases where this restriction gets in the way of the signature we want.
It would be preferable if you could have both kinds of parameters on the same function.
The only syntax change necessary is to the declaration of functions. The call syntax is unaffected, and so is noSuchMethod/Function.apply.

Equality among any two top types yields true, including composite types

Cf. these SDK issues dart-lang/sdk#33769, dart-lang/sdk#33768, requesting that Object == dynamic evaluate to true, and in general that two Type entities must be == if they both correspond to a top type, and finally that any two types which are subtypes of each other (possibly because they only differ in terms of their choice of top type in certain locations) must also be ==, e.g., List<FutureOr<void>> and List<FutureOr<FutureOr<dynamic>>>. But, in all cases, they may not necessarily be identical.

We have already said the same thing about Object and void, in generalized-void.md:

For a composite type (a generic class instantiation or a function type),
the reified representation at run time must be such that the type void and
the built-in class Object are treated as equal according to ==, but they
need not be identical
.
[Next, an example is given involving F<_, _> showing that it applies also
with composite types.]

But it seems new to me that we'd do it for dynamic as well. So we may need to consider the degree to which this will break existing code.

In favor of doing this, I'd say that it does pretty much amount to treating dynamic in the same way that we treat void: dynamic can be considered to be a variant of Object that has an extra (purely static) property that it admits usages (like member lookups or invocation) for which there is no statically known evidence that it will succeed. With void I believe that we have agreed many times that it is a variant of Object that has an extra (purely static) property that prevents certain usages (in particular, certain data flows), even though the regular semantics would have made the same constructs well-defined and acceptable if void had been replaced by Object in all the relevant types.

@lrhn has expressed support for this approach. I can see that there is a consistent way to explain why we would do so, and it seems like a nice simplification that we actually only have one top type at run time. @leafpetersen, WDYT?

Implementation tracking/meta Issue for Super Mixins

Implementation issue for unsigned/triple-shift >>> operator

This is the implementation issue for the triple-shift >>> operator (unsigned shift), see language feature #120

The operator has the same precedence as >>.
The expression e1 >>> e2 is a compile-time constant expression if both e1 and e2 are compile-time constant expressions evaluating to integers (and int will implement the operator).
The symbol #>>> is valid (along with const Symbol(">>>")), as is x >>>= e as an expression.

Adding the new operator is non-breaking.

Algebraic Data Types (ADTs, Sealed Classes, Enum with associated values)

ADTs are a very important part of any modern programming language.

Imagine you have a network call that submits login credentials. This call can:

  • log the user in
  • fail because the user entered incorrect wrong credentials
  • fail because the user doesn't have network connectivity
  • fail because of an unknown defect in your code or server broke API contract and the call failed

In Kotlin, the return value could be represented as the following sealed class

sealed class LoginResponse {
    data class Success(val authToken) : LoginResponse()
    object InvalidCredentials : LoginResponse()
    object NoNetwork : LoginResponse()
    data class UnexpectedException(val exception: Exception) : LoginResponse()
}

Kotlin's when statement unwraps this beautifully, smart casting LoginResponse.

fun on(loginResponse: LoginResponse) {
    return when (loginResponse) {
        is LoginResponse.Success -> {
            // direct access to auth token available w/o need to cast
            loginResponse.authToken
        }
        is LoginResponse.InvalidCredentials -> {

        }
        is LoginResponse.NoNetwork -> {

        }
        is LoginResponse.Unexpected -> {
            // direct access to exception available w/o need to cast
            loginResponse.exception
        }
    }
}

Sadly, languages like Java and Dart are unable to elegantly express this concept (ADTs). For a best case example, checkout out Retrofit's Response object. It is a single pojo that contains both:

Both these fields are nullable, but one must be non-null and the other must be null. To know which field can be accessed, you have to check the .isSuccessful() flag.

Now imagine the verbosity if this best case scenario has to scale to 4 or more possible results. A whole new enum class would have to be added to work around it...it's not pretty.

Please add support for ADTs to Dart.

note: As an Android developer, I find that Flutter is an interesting proposition that is currently held back by the language.

Add binary integer literals

This issue was originally filed by [email protected]


When working with bits and bytes, having binary integer literals comes handy, as a lot of languages acknowledged (Python, Ruby, Java, even GCC has them as an extension). I believe that the reasoning behind adding binary literals to Java 7, as per http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000929.html, is equally valid to Dart, and therefore I propose to add binary literals in the same form ("0b100000" as an equivalent of "0x20"). I think that implementation is no concern (I did it in the VM and it was pretty straightforward, as I describe in http://ladicek.github.com/2012/04/29/hacking-the-vm-adding-binary-integer-literals.html).

Infer generics in `is` check for type promotion

Consider following code:

abstract class A<T> {}
abstract class B<T> implements A<T> {
  b();
}

void foo<T>(A<T> a) {
  if (a is B) {
    return a.b();
  }
}

It gives error message:

// The method 'b' isn't defined for the class 'A'.

so it seems the type promotion on a didn't happen inside the if block. To fix this I can change the condition to be: if (a is B<T>).

This somewhat makes sense, but it seems to me that in this case compiler could infer that B must be B<T>, if it's casted from A<T>. I guess it could be some subclass of T, but that would still be substitutable for B. Or maybe there are some other edge cases in general. If this is not possible then maybe compiler could give some hint why type promotion didn't happen?

Feature Request: Variable Number of Arguments

This issue was originally filed by [email protected]


It would be nice to have variable numbers of arguments, a la python splats.

This would make porting python code to dart much cleaner.

For example, I would like to do something like this:

void myFunc(arg1, arg2, *anyRemainingArgs){
  var extraArg1 = anyRemainingArgs[0];
  var extraArg2 = anyRemainingArgs[1];
  ...
}

Add data classes

Immutable data are used heavily for web applications today, commonly with Elm-like (redux, ngrx, ...) architectures. Most common thing web developer is doing with data is creating a copy of it with some fields changed, usually propagated to the root of state tree. JavaScript has spread operator for this. There should be a easy way to use immutable data structures in Dart. I would like to have data classes (inspired by Kotlin) in Dart. Possible API:

data class User {
  String name;
  int age;
}

Compiler assumes that all fields of data class are immutable. Compiler adds equals implementation based on shallow equals, hashCode implementation based on mix of all object fields, toString implementation of the form <Type> <fieldN>=<valueN>, and copy function to recreate object with some fields changed.

You may argue that there is already Built value package that allows to do similar things, but we have many issues with package, mainly:

  1. It requires writing a lot of boilerplate code
  2. It requires running watcher/manual code generation during development.
  3. It requires saving generated files to repository because code generation time is too large for big applications.

I have found that using built value actually decreases my productivity and I do my work faster with even manually writing builders for classes.

If data classes would be implemented on language level, it would increase developer productivity and optimizations can be made when compiling code to particular platform.

Add syntax for character code constants.

The currently simplest way to get the character code of the character '9' is either "9".charCodeAt(0), which isn't even constant, or writing the number constant, e.g., 0x39. Or define a whole slew of constants as in the dart2js characters.dart library:<http://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/lib/compiler/implementation/util/characters.dart>

It would improve readability and usability a lot if there was a simple way to specify "the character code of the character _", like C and Java's '9', Ruby's ?9, Scheme's #\9 or SML's #"9".

I propose the SML syntax since it doesn't collide with any other Dart syntax, and it could allow arbitrary Dart single-character string literals, piggy-backing on known string syntax, so it's possible to write, e.g., #"\n" instead of 0x10.
It should only work as a literal, no #stringVar or multi-character strings, or other funky stuff.

I don't want a character type, just a way to create integer constants that is both readable, simple and compile-time constant.

Add possibility to consistently get object property name

We need to get object property name in cases like Angular 2 ngOnChanges (when you need to compare key from changes map with object property name):

ngOnChanges(Map<String, SimpleChange> changes);
It would be great to have something like nameof in C#, so that we could get property name and don't be afraid that someone will rename property.

nameof(obj.propertyName); // 'propertyName'

Experiment - Class annotation for disallowing use as a super-type, a.k.a "sealed classes"

Users have expressed a desire for the ability to mark a class as "final" or "sealed," so that it is unavailable as a super-class. That is, it is an error for a class to extend, implement, or mix in a "sealed class." Specifically, the Angular Dart team wishes to seal certain classes that have very undefined behavior when users subclass.

There is good discussion on an MVP, make-something-cheap-available-to-users request for a @sealed annotation.

An experiment

All that is being suggested here, after some discussion 1, is an annotation in the meta package, @sealed, enforced with some analyzer Hint codes.

Use cases

The primary use case is to remove burden from, and give assurance to, authors of public classes. Today, as all classes are "open," there is an expectation from users that they can extend, implement, and mix in classes from other packages safely. For this to be true, authors must actually have extensions, implementations, and mix ins in mind, unless they write "DO NOT EXTEND/IMPLEMENT" in their Documentation comments.

A "sealed classes" feature can remove burden from authors by allowing for a class that doesn't need to support the possibility that other classes use it as a super-class. Authors can write a class

  • whose methods call other public methods,
  • whose methods refer to private members,

without worrying about how to support sub-classes.

A "sealed classes" feature can give assurance to an author, when considering how a change to the class will affect users. An author can:

  • change a method to refer to a public method,
  • add public methods,

without worrying about how the change will affect potential sub-classes.

Definition

A sealed class, one which has been annotated with @sealed from the meta package, can only be extended by, implemented by, or mixed into a class declared in the same "package" (to be defined) as the sealed class. The consequences of "sealing" a class are primarily in static analysis, and would be implemented in the analyzer package:

  • It is a Hint if the @sealed annotation occurs on anything other than a class.
  • Given a class C declared in a package P,
    • It is a Hint if a class which is declared in a package other than P extends, implements, or mixes in C.
    • It is a Hint if a class, declared in P, and either extending, implementing, or mixing in C, is not also annotated with @sealed.

Why a Hint?

All analyzer codes that do not arise from text found in the spec are HINT codes (well, except TODO and LINT, and STRONG_MODE_* codes). Since this is the most formal proposal for an annotation that I know of, perhaps it will pass enough muster to be listed as an ERROR or WARNING... if there is such a request, this can be specified at such time. (@MichaelRFairhurst requested below not to use error.)

Ignorable?

All analyzer codes are ignorable with an // ignore comment. The above Hints will also be ignorable:

  • // ignore: USE_OF_SEALED_SUPER_CLASS will allow a sealed class to be used as a super-class outside of the library in which it is declared.
  • // ignore: OPEN_SEALED_SUB_CLASS will allow a non-sealed class to sub-class a sealed class from within the library in which each is declared.

(@MichaelRFairhurst requested below to allow these analyzer results to be ignorable.)

Alternative definitions

Library-level sub-classes

Library-level sub-classes would be very similar to package-level subclasses, but would be more restrictive. Library-level sub-classes make an earlier suggestion of performance experiments easier, but the performance experiments are now a non-goal of this annotation. Members of the Dart team feel that a package boundary is the more natural boundary for something that authors create for themselves; typically the authors of a package "own" the whole package, rather than distinct pieces.

Additionally, new visibility mechanisms were suggested; maybe Dart can support an "open part" (as @eernstg suggests below or "friend library" (as I suggest in a comment on testing private methods). The part/friend concept would help back-ends close the list of all sub-classes, but we don't have this concept yet, so cannot experiment yet.

Single concrete class

The "sealed classes" feature originally restricted sealed classes to be un-extendable, un-implementable, and unable to be mixed in, by any class anywhere ("final classes"). @eernstg argues below that the reasons for making a "final class" are different from those for making a "sealed class," and that it would not be very meaningful to switch the definition of @sealed from one to the other.

Since Angular Dart can make use of library-sealed just as easily, and back-ends like the VM can optimize just as easily, then we use the library-sealed definition.

Isn't "experiment" just another word for "I don't want to go through the trouble of actual approval?"

We actually do want to experiment. Real world usage can help the language team steer towards correct choices later:

  1. Are sealed classes useful?
  2. Do public class authors "abuse" sealed classes? ("Abuse" might mean "very defensively program with." We want to see how sealed is used.)
  3. Do users // ignore the "sealed classes" Hints?
  4. Do users fork sealed classes in order to unseal them?
  5. Is the "sealed" concept useful as the single great class modifier affecting sub-classing? Do users basically use it as "final?"

Depending on the answers to the above, "sealed classes" may be shelved, or implemented with the same definition as the annotation, or may be implemented with changes. Other features may be implemented or experimented with, such as final classes, sealed-by-default, open, noImplicitInterface, etc.

Cost of rolling back the experiment

@munificent points out below that asserts-in-initializers and supermixins were both "experiments" that did not smoothly transition to full support; we should try to avoid a similarly bumpy road.

If the @sealed experiment "fails", i.e. the team decides it should be rolled back, it can be done so without much fanfare. Rolling back the feature means removing enforcement from the analyzer (and any back-ends with performance experiments based on the annotation). A Dart release will include a release note saying something to the effect of "Any sealed classes may now be sub-classed at will; don't rely on them not being sub-classed."

Path towards a language feature

For package-level "sealed classes" to graduate from an experimental annotation to a language feature (like a class modifier), a definition for package will first need to enter the language. There is currently no effort towards defining such a thing, but there is motivation from several facets of Dart to make one.

Prior art, discussion

Java

A case of prior art / prior discussion, Joshua Bloch, once Google's chief Java architect and author of Effective Java,
wrote in Effective Java,

Design and document for inheritance or else prohibit it.

Joshua goes on to explain the work involved in properly designing, implementing,
and testing a class that is subclassable, which is substantial. When Joshua writes "or else prohibit it," he is referring to the use of either (a) marking a class final or (b) using only private constructors. The private constructor solution, (b), does not work for Dart today, as all classes have implicit interfaces which can be implemented. A "no implicit interface" modifier could be a sibling feature to this "sealed classes" feature, but I consider it far out of scope.

Kotlin

Kotlin has a more advanced feature set regarding "sealing" or "finalizing" classes. Here's a quick rundown:

  • By default, a class is "final," such that it cannot be subclassed. This is directly motivated by Joshua Bloch's writing. To make a class open for subclassing, the open modifier must be applied.
  • A class can be "sealed" with the sealed modifier. This means that the class can only be sub-classed within the file where it is declared. This has the effect that the author immediately knows all of the possible direct sub-classes, and can use this knowledge in switch statements; you can cover every possibility of sub-classes with a finite and immediately known set.
  • One more note: the sub-classes of a sealed class have no restrictions different from that of other classes; they can be marked as open, and they can be sub-classed when open.

I really like these two similar but distinct features. For a sealed class, the ultimate set of concrete classes with a sealed class as a super-class cannot be known, unless all direct sub-classes are "closed." This property is under the author's control; if the author just wants to know all direct sub-classes, for use in, e.g. a switch statement, (and if they're willing to support the idea of sub-classing), then they can mark sub-classes as open. Otherwise, if they want to know every concrete class with a sealed class as a super-class, they can leave sub-classes closed, and not have to support the concept of sub-classing.

Other languages

Other languages, in addition to Java and Kotlin, have a similar / identical feature, either "sealed" or "final" classes, including C++, C#, and Swift. Neither JavaScript nor TypeScript have a similar feature.

Footnotes

1 Initially, I did not predict the level of discussion that this feature request would raise. Initially, I thought that the experimental @sealed annotation would land quickly and quietly. This feature request was initially for a language feature, "sealed classes." After seeing all of the issues being raised, and some thinking that this was just a proposal for the experimental annotation, I've decided to make it that.

Inference should flow information between arguments in a generic function call

EDIT by @jmesserly

Inference should flow information up/down between arguments in a generic function call. This will help inference common methods like Iterable.fold and Future.forEach.


(original description)
We currently do downwards and upwards inference independently. This gets us a fair bit, but we should eventually integrate the two, probably following the colored local type inference approach.

Add a function pipe operator

This issue was originally filed by @chalin


Both AngularDart and Polymer have special syntax in support of function pipes (for Filters and Transformers) through the pipe | (bar) operator. E.g.

    person.lastName | uppercase

It would seem a natural fit for Dart to have native support for such a pipe operator. A case for the addition of a pipe operator in Dart is given here: http://pchalin.blogspot.com/2014/02/case-for-pipe-operator-in-dart.html

As is mentioned in the blog entry, I believe that added support should be considered for function composition operators (forward and/or reverse) as well.

Nested typedefs

Currently typedef can only appear at the toplevel. If I have a class that I want to allow a user supplied callback and maintain type checking, there currently is no way:

class Foo<T> {
  Function _callback;
  Foo(void callback(T something)) : this._callback = callback;

  doSomething() {
    ... something ...
    _callback(result);
  }
}

Support shorthand class creation syntax

For classes with a single constructor that takes in a few values.

class User {
  final String firstName;
  final String lastName;

  User({@required this.firstName, @required this.lastName});
}

It would be much cleaner and concise to simply allow a "primary constructor":

class User({@required this.firstName, @required this.lastName}) {
  final String firstName;
  final String lastName;
}

Duplicating constructor and class declaration seems a little clunky. When we have more fields,
this duplication is further known and wish we could take it a step further to allow:

class User({@required final String firstName, @required final String lastName});

Less duplication in code will lead to nice details in our widgets for flutter like from this:

class AppDetails extends StatefulWidget {
  final String url;
  final String jobName;

  AppDetails({@required this.url, @required this.jobName});

  @override
  AppDetailsState createState()  => new AppDetailsState(this.url, initialJobName: jobName);
}

class AppDetailsState extends State<AppDetails> {
  String selectedJobName;
  final String jobUrl;

  AppDetailsState(this.jobUrl, {@required String initialJobName}) {
    this.selectedJobName = initialJobName;
  }

  // state implementation below

}

to this:

class AppDetails({@required final String url, @required final String jobName})
 extends StatefulWidget {
  @override
  AppDetailsState createState() => new AppDetailsState(this.url, initialJobName: jobName);
}

class AppDetailsState(final String jobUrl, {@required String initialJobName}) 
 extends State<AppDetails> {

  // state implementation below

from 17 lines of boilerplate to 8.

Digit separators in number literals.

Solution to #1.

To make long number literals more readable, allow authors to inject digit group separators inside numbers.
Examples with different possible separators:

100 000 000 000 000 000 000  // space 
100,000,000,000,000,000,000  // comma
100.000.000.000.000.000.000  // period
100'000'000'000'000'000'000  // apostrophe (C++)
100_000_000_000_000_000_000  // underscore (many programming languages).

The syntax must work even with just a single separator, so it can't be anything that can already validly seperate two expressions (excludes all infix operators and comma) and should already be part of a number literal (excludes decimal point).
So, the comma and decimal point are probably never going to work, even if they are already the standard "thousands separator" in text in different parts of the world.

Space separation is dangerous because it's hard to see whether it's just space, or it's an accidental tab character. If we allow spacing, should we allow arbitrary whitespace, including line terminators? If so, then this suddenly become quite dangerous. Forget a comma at the end of a line in a multiline list, and two adjacent integers are automatically combined (we already have that problem with strings). So, probably not a good choice, even if it is the preferred formatting for print text.

The apostrope is also the string single-quote character. We don't currently allow adjacent numbers and strings, but if we ever do, then this syntax becomes ambiguous. It's still possible (we disambiguate by assuming it's a digit separator). It is currently used by C++ 14 as a digit group separator, so it is definitely possible.

That leaves underscore, which could be the start of an identifier. Currently 100_000 would be tokenized as "integer literal 100" followed by "identifier _000". However, users would never write an identifier adjacent to another token that contains identifier-valid characters (unlike strings, which have clear delimiters that do not occur anywher else), so this is unlikely to happen in practice. Underscore is already used by a large number of programming languages including Java, Swift, and Python.

We also want to allow multiple separators for higher-level grouping, e.g.,:

100__000_000_000__000_000_000

For this purpose, the underscore extends gracefully. So does space, but has the disadvantage that it collapses when inserted into HTML, whereas '' looks odd.

For ease of reading and ease of parsing, we should only allow a digit separator that actually separates digits - it must occur between two digits of the number, not at the end or beginning, and if used in double literals, not adjacent to the . or e{+,-,} characters, or next to an x in a hexadecimal literal.

Examples

100__000_000__000_000__000_000  // one hundred million million millions!
0x4000_0000_0000_0000
0.000_000_000_01
0x00_14_22_01_23_45  // MAC address
555_123_4567  // US Phone number

Invalid literals:

100_
0x_00_14_22_01_23_45 
0._000_000_000_1
100_.1
1.2e_3

An identifier like _100 is a valid identifier, and _100._100 is a valid member access. If users learn the "separator only between digits" rule quickly, this will likely not be an issue.

Implementation issues

Should be trivial to implement at the parsing level. The only issue is that a parser might need to copy the digits (without the separators) before calling a parse function, where currently it might get away with pointing a native parse function directly at its input bytes.
This should have no effect after the parsing.

Style guides might introduce a preference for digit grouping (say, numbers with more than six digits should use separators) so a formatter or linter may want access to the actual source as well as the numerical value. The front end should make this available for source processing tools.

Library issues

Should int.parse/double.parse accept inputs with underscores. I think it's fine to not accept such input. It is not generated by int.toString(), and if a user has a string containing such an input, they can remove underscores manually before calling int.parse. That is not an option for source code literals.
I'd prefer to keep int.parse as efficient as possible, which means not adding a special case in the inner loop.
In JavaScript, parsing uses the built-in parseInt or Number functions, which do not accept underscores, so it would add (another) overhead for JavaScript compiled code.

Related work

Java digit separators.

Add `name` instance property to enum values

This issue was originally filed by @seaneagan


With the current spec it's difficult to get the name of an enum value, since the toString() result is prefixed with EnumClass.:

enumName(enumValue) {
  var s = enumValue.toString();
  return s.substring(s.indexOf('.') + 1);
}

It would be nice to just be able to do:

enumValue.name

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.