Dart Language and Library Newsletter (2017-08-18)

97 views
Skip to first unread message

Florian Loitsch

unread,
Aug 18, 2017, 12:29:32 PM8/18/17
to General Dart Discussion
Github link: https://github.com/dart-lang/sdk/blob/e6e8d35323dbc431d9b9aed32b20d5462702999f/docs/newsletter/20170818.md

Earlier newsletters:
https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md

Dart Language and Library Newsletter

Welcome to the Dart Language and Library Newsletter.

If you missed it

Did you know that you can write trailing commas to arguments and parameters? This feature was added to the specification about a year ago.

It's main use-case is to unify parameter and argument lists that span multiple lines. For example, Flutter uses it extensively to keep tree-like instantiations nicely aligned:

    return new Material(
      // Column is a vertical, linear layout.
      child: new Column(
        children: <Widget>[
          new MyAppBar(
            title: new Text(
              'Example title',
              style: Theme.of(context).primaryTextTheme.title,
            ),
          ),
          new Expanded(
            child: new Center(
              child: new Text('Hello, world!'),
            ),
          ),
        ],
      ),

Note how every argument list ends with a comma. The dartfmt tool knows about these trailing commas and ensures that the individual entries stay on their own lines so that it is easy to move them around with cut and paste.

Recently, we also updated the specification to allow trailing commas in asserts. This makes the syntax of asserts more consistent with function calls.

Function Type Syntax

A few months ago, we added a new function type syntax to Dart (we mentioned it in our first newsletter).

// Examples:
typedef F = void Function(int);  // A void function that takes an int.

void foo(T Function<T>(T x) f) {  // foo takes a generic function.
  ...
} 

class A {
  // A has a field `f` of type function that takes a String and returns void.
  void Function(String) f;
}

Before we added the new function-type syntaxes, we evaluated multiple options. In this section I will summarize some of the discussions we had.

Motivation

The new function-type syntax intends to solve three issues:

  1. the old typedef syntax doesn't support generic types.
  2. the old function syntax can't be used for fields and locals.
  3. in the old syntax, providing only one identifier in an argument position is interpreted as name and not type. For example: typedef f(int); is not a typedef for a function that expects an int, but for a function that expects dynamic and names the argument "int".

With Dart 2.0 we will support generic methods, and also generic closures. This means that a function can accept a generic function as argument. We were lacking a way to express this type.

Dart 1.x has two ways to express function types: a) an inline syntax for parameters and b) typedefs.

It was easy to extend the inline syntax to support generic arguments:

// Takes a function `factoryForA` that is generic on T.
void foo(A<T> factoryForA<T>()) {
  A<int> x = factoryForA<int>();
  A<String> x = factoryForA<String>();
}

However, there was no easy way to do the same for typedefs:

typedef A<T> FactoryForA<T>();// Does *not* do what we want it to do:

FactoryForA f;  // A function that returns an `A<dynamic>`.
FactoryForA<String> f2;  // A function that returns an `A<String>`.
f<int>();  // Error: `f` is not generic.

We had already used the most consistent place for the generic method argument as a template argument to the typedefitself. If we could go back in time, we could change it as follows:

typedef<T> List<T> TemplateTypedef();
TemplateTypedef<int> f;  // A function that returns a List<int>.
TemplateTypedef f;  // A function that returns a List<dynamic>.

typedef List<T> GenericTypedef<T>();
GenericTypedef f;  // A function that is generic.
List<int> ints = f<int>();
List<String> strings = f<String>();

Given that this would be a breaking change we explored alternatives that would also solve the other two issues. In particular the new syntax had to work for locals and fields, too.

First and foremost the new syntax had to be readable. It also had to solve the three mentioned issues. Finally, we wanted to make sure, we didn't choose a syntax that would hinder future evolution of the language. We made sure that the syntax would work with:

  • nullability: the syntax must be nullable without too much hassle:
     (int)->int?;  // A function that is nullable, or that returns a nullable integer?
     Problem disappears with <-
     int <- (int)? ; vs int? <- (int) 
  • union types (in case we ever want them).

Common Characteristics

For all the following proposals we had decided that the arguments could either be just the type, or optionally have a name. For example, (int)->int is equivalent to (int id)->int. Especially with multiple arguments of the same type, providing a name can make it much easier to reason about the type: (int id, int priority) -> void. However, type-wise these parameter names are ignored.

All of the proposals thus interpret single-argument identifiers as types. This is in contrast to the old syntax where a single identifier would state the name of the parameter: in void foo(bar(int)) {...} the int is the name of the parameter to bar. This discrepancy is hopefully temporary, as we intend to eventually change the behavior of the old syntax.

Right -> Arrow

Using -> as function-type syntax feels very natural and is used in many other languages: Swift, F#, SML, OCaml, Haskell, Miranda, Coq, Kotlin, and Scala (with =>).

Examples:

typedef F = (int) -> void;  // Function from (int) to void.
typedef F<T> = () -> List<T>;  // Template Typedef.
typedef F = <T>(T) -> List<T>;  // Generic function from T to List<T>.

We could even allow a short form when there is only one argument: int->int.

We have experimented with this syntax: [https://codereview.chromium.org/2439573003/]

Advantages:

  • easy to learn and familiar to many developers.
  • could support shorthand form int->int.

Open questions:

  • support shorthand form?
  • whitespace. Should it be (int, int) -> String or (int, int)->String, etc.

Disadvantages:

  • Relatively late token. The parser would have to do relatively big lookaheads.
  • Works badly with nullable types:
    typedef F = (int) -> int?;  // Nullable function or nullable int?
    // Could be disambiguated as follows:
    typedef F = ((int)->int)?;   // Clearly nullable function.
Left <- Arrow

This section explores using <- as function-type syntax. There is at least one other language that uses this syntax: Twelf.

Examples:

typedef F = void <- (int);  // Function from (int) to void.
typedef F<T> = List<T> <- ();  // Template Typedef.
typedef F = List<T> <- <T>(T);  // Generic function from T to List<T>.

Could also allow a short form: int<-int. (For some reason this seems to read less nicely than int->int.)

We have experimented with this syntax: [https://codereview.chromium.org/2466393002/]

Advantages:

  • return value is on the left, similar to normal function signatures. This also simplifies typedefs, where the return value is more likely to stay on the first line.
  • faster to parse, since the <- doesn't require a lot of look-ahead.
  • relatively similar to ->.
  • no problems with nullable types:
    typedef F = int <- (int)?;  // Nullable function.
    typedef F = int? <- (int);  // Returns nullable int.

Open Questions:

  • whitespace?
  • support shorthand form?

Disadvantages:

  • <- is ambiguous: x<-y ? foo(x) : foo(y) // if x < (-y) ....
  • Not as familiar as ->.
Function

Dart already uses Function as general type for functions. It is relatively straightforward to extend the use of Function to include return and parameter types. (And no: it's not Function<int, int> since that wouldn't work for named arguments).

typedef F = void Function(int);  // Function from (int) to void.
typedef F<T> = List<T> Function();  // Template Typedef.
typedef F = List<T> Function<T>(T);  // Generic function from T to List<T>.

This form does not allow any shorthand syntax, but fits nicely into the existing parameter syntax.

Before we accepted this syntax, we had experimented with this syntax: [https://codereview.chromium.org/2482923002/]

Advantages:

  • very similar to the syntax of the corresponding function declarations.
  • no ambiguity.
  • (almost) no new syntax. That is, the type can be immediately extrapolated from other syntax.
  • no open questions wrt whitespace.
  • symmetries with existing use of Function:
    Function f;  // a function.
    Function(int x) f;  // a function that takes an int.
    double Function(int) f;  // a function that takes an int and returns a double.

Disadvantages:

  • longer.
Conclusion

We found that the Function-based syntax fits nicely into Dart and fulfills all of our requirements. Due to its similarity to function declarations it is also very future-proof. Any feature that works with function declarations should work with the Function-type syntax, as well.

aggi...@gmail.com

unread,
Aug 24, 2017, 8:06:16 PM8/24/17
to Dart Misc
really don't understand the syntax in the given examples for the new Function types.

// Examples:

void foo(T Function<T>(T x) f) {

 
...
}


I think I understand that T means the type returned is the same as the type passed, but what's up with the diamond and f? It just looks why too busy. Maybe it could be broken up by defining the different parts separating?

rym...@gmail.com

unread,
Aug 24, 2017, 9:01:50 PM8/24/17
to Dart Misc
You just need to think about it differently.

Function arguments in Dart are always "name value". So:

Function<T>(T x) f

is declaring an argument "f" of type "Function<T>(T x)". As for the type itself, it's:

Function<return>(args...)

This is just using the normal generics syntax like List<int> does.


--
Ryan (ライアン)
Yoko Shimomura, ryo (supercell/EGOIST), Hiroyuki Sawano >> everyone else
http://refi64.com
--
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.

aggi...@gmail.com

unread,
Aug 24, 2017, 11:18:18 PM8/24/17
to Dart Misc
The example was actually:

T Function<T>(T x) f

So is it defining the return type twice? I haven't actually used this and don't see a use for it other than to be like JavaScript. Which I don't think Dart needs to emulate. But I digress.

rym...@gmail.com

unread,
Aug 24, 2017, 11:36:14 PM8/24/17
to Dart Misc
Ohhh that makes more sense. I'm sorry; I mixed it up.

Just mentally replace "function" with a function name and it'll all make sense:


T Function<T>(T x)
T myfunc<T>(T x)


It's just the type of a generic function.

FWIW the main "use case" is mostly consistency: this is an actual type, whereas the old syntax mixed the type with the name.



--
Ryan (ライアン)
Yoko Shimomura, ryo (supercell/EGOIST), Hiroyuki Sawano >> everyone else
http://refi64.com

Erik Ernst

unread,
Aug 25, 2017, 3:29:24 AM8/25/17
to Dart Misc
On Fri, Aug 25, 2017 at 5:36 AM, rym...@gmail.com <rym...@gmail.com> wrote:
Ohhh that makes more sense. I'm sorry; I mixed it up.

Just mentally replace "function" with a function name and it'll all make sense:


T Function<T>(T x)
T myfunc<T>(T x)


It's just the type of a generic function.

Right, the fact that function types are so similar to function declarations was the main motivation for having this syntax, even though it's not the most concise choice.

FWIW the main "use case" is mostly consistency: this is an actual type, whereas the old syntax mixed the type with the name.

Also the ability to use function types in a larger number of situations (such as `List<void Function(int)>` and `int Function() f = () => 42;`) without a typedef.



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

Reply all
Reply to author
Forward
0 new messages