[dart-announce] Dart 1.22 is now available

46 views
Skip to first unread message

'Kevin Moore' via Dart Announcements

unread,
Feb 14, 2017, 1:03:56 PM2/14/17
to Dart Announcements
Check out the announcement and the changelog

1.22.0

Language

  • Breaking change: 'Generalized tear-offs' are no longer supported, and will cause errors. We updated the language spec and added warnings in 1.21, and are now taking the last step to fully de-support them. They were previously only supported in the VM, and there are almost no known uses of them in the wild.

  • The assert() statement has been expanded to support an optional second message argument (SDK issue 27342).

    The message is displayed if the assert fails. It can be any object, and it is accessible as AssertionError.message. It can be used to provide more user friendly exception outputs. As an example, the following assert:

    assert(configFile != null, "Tool config missing. Please see https://goo.gl/k8iAi for details.");

    would produce the following exception output:

    Unhandled exception:
    'file:///Users/mit/tmp/tool/bin/main.dart': Failed assertion: line 9 pos 10:
    'configFile != null': Tool config missing. Please see https://goo.gl/k8iAi for details.
    #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:33)
    #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:29)
    #2      main (file:///Users/mit/tmp/tool/bin/main.dart:9:10)
    
  • The Null type has been moved to the bottom of the type hierarchy. As such, it is considered a subtype of every other type. The null literal was always treated as a bottom type. Now the named class Null is too:

    const empty = <Null>[];
    
    String concatenate(List<String> parts) => parts.join();
    int sum(List<int> numbers) => numbers.fold(0, (sum, n) => sum + n);
    
    concatenate(empty); // OK.
    sum(empty); // OK.
  • Introduce covariant modifier on parameters. It indicates that the parameter (and the corresponding parameter in any method that overrides it) has looser override rules. In strong mode, these require a runtime type check to maintain soundness, but enable an architectural pattern that is useful in some code.

    It lets you specialize a family of classes together, like so:

    abstract class Predator {
      void chaseAndEat(covariant Prey p);
    }
    
    abstract class Prey {}
    
    class Mouse extends Prey {}
    
    class Seal extends Prey {}
    
    class Cat extends Predator {
      void chaseAndEat(Mouse m) => ...
    }
    
    class Orca extends Predator {
      void chaseAndEat(Seal s) => ...
    }

    This isn't statically safe, because you could do:

    Predator predator = new Cat(); // Upcast.
    predator.chaseAndEat(new Seal()); // Cats can't eat seals!

    To preserve soundness in strong mode, in the body of a method that uses a covariant override (here, Cat.chaseAndEat()), the compiler automatically inserts a check that the parameter is of the expected type. So the compiler gives you something like:

    class Cat extends Predator {
      void chaseAndEat(o) {
        var m = o as Mouse;
        ...
      }
    }

    Spec mode allows this unsound behavior on all parameters, even though users rarely rely on it. Strong mode disallowed it initially. Now, strong mode lets you opt into this behavior in the places where you do want it by using this modifier. Outside of strong mode, the modifier is ignored.

  • Change instantiate-to-bounds rules for generic type parameters when running in strong mode. If you leave off the type parameters from a generic type, we need to decide what to fill them in with. Dart 1.0 says just use dynamic, but that isn't sound:

    class Abser<T extends num> {
       void absThis(T n) { n.abs(); }
    }
    
    var a = new Abser(); // Abser<dynamic>.
    a.absThis("not a num");

    We want the body of absThis() to be able to safely assume n is at least a num -- that's why there's a constraint on T, after all. Implicitly using dynamic as the type parameter in this example breaks that.

    Instead, strong mode uses the bound. In the above example, it fills it in with num, and then the second line where a string is passed becomes a static error.

    However, there are some cases where it is hard to figure out what that default bound should be:

    class RuhRoh<T extends Comparable<T>> {}

    Strong mode's initial behavior sometimes produced surprising, unintended results. For 1.22, we take a simpler approach and then report an error if a good default type argument can't be found.

Core libraries

  • Define FutureOr<T> for code that works with either a future or an immediate value of some type. For example, say you do a lot of text manipulation, and you want a handy function to chain a bunch of them:

    typedef String StringSwizzler(String input);
    
    String swizzle(String input, List<StringSwizzler> swizzlers) {
      var result = input;
      for (var swizzler in swizzlers) {
        result = swizzler(result);
      }
    
      return result;
    }

    This works fine:

    main() {
      var result = swizzle("input", [
        (s) => s.toUpperCase(),
        (s) => () => s * 2)
      ]);
      print(result); // "INPUTINPUT".
    }

    Later, you realize you'd also like to support swizzlers that are asynchronous (maybe they look up synonyms for words online). You could make your API strictly asynchronous, but then users of simple synchronous swizzlers have to manually wrap the return value in a Future.value(). Ideally, your swizzle() function would be "polymorphic over asynchrony". It would allow both synchronous and asynchronous swizzlers. Because awaitaccepts immediate values, it is easy to implement this dynamically:

    Future<String> swizzle(String input, List<StringSwizzler> swizzlers) async {
      var result = input;
      for (var swizzler in swizzlers) {
        result = await swizzler(result);
      }
    
      return result;
    }
    
    main() async {
      var result = swizzle("input", [
        (s) => s.toUpperCase(),
        (s) => new Future.delayed(new Duration(milliseconds: 40), () => s * 2)
      ]);
      print(await result);
    }

    What should the declared return type on StringSwizzler be? In the past, you had to use dynamic or Object, but that doesn't tell the user much. Now, you can do:

    typedef FutureOr<String> StringSwizzler(String input);

    Like the name implies, FutureOr<String> is a union type. It can be a String or a Future<String>, but not anything else. In this case, that's not super useful beyond just stating a more precise type for readers of the code. It does give you a little better error checking in code that uses the result of that.

    FutureOr<T> becomes really important in generic methods like Future.then(). In those cases, having the type system understand this magical union type helps type inference figure out the type argument of then() based on the closure you pass it.

    Previously, strong mode had hard-coded rules for handling Future.then() specifically. FutureOr<T> exposes that functionality so third-party APIs can take advantage of it too.

Tool changes

  • Dart2Js

    • Remove support for (long-time deprecated) mixin typedefs.
  • Pub

    • Avoid using a barback asset server for executables unless they actually use transformers. This makes precompilation substantially faster, produces better error messages when precompilation fails, and allows globally-activated executables to consistently use the Isolate.resolvePackageUri() API.

    • On Linux systems, always ignore packages' original file owners and permissions when extracting those packages. This was already the default under most circumstances.

    • Properly close the standard input stream of child processes started using pub run.

    • Handle parse errors from the package cache more gracefully. A package whose pubspec can't be parsed will now be ignored by pub get --offline and deleted by pub cache repair.

    • Make pub run run executables in spawned isolates. This lets them handle signals and use standard IO reliably.

    • Fix source-maps produced by dart2js when running in pub serve: URL references to assets from packages match the location where pub serve serves them (packages/package_name/ instead of../packages/package_name/).

Infrastructure changes

  • The SDK now uses GN rather than gyp to generate its build files, which will now be exclusively ninja flavored. Documentation can be found on our wiki. Also see the help message of tools/gn.py. This change is in response to the deprecation of gyp. Build file generation with gyp will continue to be available in this release by setting the environment variable DART_USE_GYP before running gclient sync or gclient runhooks, but this will be removed in a future release.

--
For more news and information, visit https://plus.google.com/+dartlang
 
To join the conversation, visit https://groups.google.com/a/dartlang.org/
Reply all
Reply to author
Forward
0 new messages