Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Records & Patterns: A productivity hack in Dart 3

Records & Patterns: A productivity hack in Dart 3

Dart 3 introduces two new features that can significantly improve your productivity: records and patterns. Records are a new data type that lets you group together multiple values into a single, named object. Patterns are a new way to match and destructure values, making it easier to work with complex data structures.

In this talk, we will explore how records and patterns can be used to write more concise, readable, and maintainable code. We will also look at some of the new features that are available in Dart 3, such as class modifiers and pattern matching on generic types.

By the end of this talk, you will have a good understanding of how records and patterns can be used to improve your productivity when writing Dart code.

Audience:

This talk is intended for developers who are familiar with the Dart programming language. No prior experience with records or patterns is required.

Etornam Sunu Bright

June 12, 2023
Tweet

More Decks by Etornam Sunu Bright

Other Decks in Programming

Transcript

  1. Dart 3 is the latest version of the Dart programming

    language. It introduces a number of new features that make it more reliable and productive. • Records • Patterns Features
  2. Records are a new type in Dart that lets you

    aggregate multiple values of different types in a single function return. This makes it easier to work with structured data. Records are similar to classes in that they can contain fields, but they are different in several ways. Records are anonymous, meaning that they do not have a name. They are also immutable, meaning that their fields cannot be changed after they are created. Finally, records are structurally typed, meaning that their type is determined by the types of their fields. What are Records? Records
  3. final record = ('first', a: 2, b: true, 'last'); //Record

    type annotations are comma-delimited lists of //types enclosed in parentheses. // Returns multiple values in a record: (String, int) userInfo(Map<String, dynamic> json) { return (json['name'] as String, json['age'] as int); } final json = <String, dynamic>{ 'name': 'Dash', 'age': 10, 'color': 'blue', }; // Destructures using a record pattern: var (name, age) = userInfo(json); /* Equivalent to: var info = userInfo(json); var name = info.$1; var age = info.$2; */
  4. Patterns are a syntactic category in the Dart language, like

    statements and expressions. A pattern represents the shape of a set of values that it may match against actual values. They let you match and destructure values. This makes it easier to work with complex data structures. What are Patterns? Patterns
  5. ///Many patterns make use of subpatterns, sometimes called ///outer and

    inner patterns const a = '1'; const b = '2'; switch (obj) { // List pattern [a, b] matches obj first if obj is a list with two fields, // then if its fields match the constant subpatterns 'a' and 'b'. case [a, b]: print('$a, $b'); } ///Destructuring var numList = [1, 2, 3]; // List pattern [a, b, c] destructures the three elements from numList... var [a, b, c] = numList; // ...and assigns them to new variables. print(a + b + c);
  6. /// Variable declaration // Declares new variables a, b, and

    c. final (a, [b, c]) = ('str', [1, 2]); ///Why use patterns? //Without patterns, validation is verbose: var json = { 'user': ['Lily', 13] }; if (json is Map<String, Object?> && json.length == 1 && json.containsKey('user')) { var user = json['user']; if (user is List<Object> && user.length == 2 && user[0] is String && user[1] is int) { var name = user[0] as String; var age = user[1] as int; print('User $name is $age years old.'); } } //Validating incoming JSON var json = { 'user': ['Lily', 13] }; var {'user': [name, age]} = json;
  7. Sound null safety is a feature in Dart that prevents

    null pointer exceptions at runtime. It does this by making all variables non-nullable by default. This means that if you try to assign a null value to a variable, the compiler will throw an error. Sound null safety is a major improvement over the previous null safety model in Dart, which allowed variables to be nullable by default. What is sound null safety? Sound null safety
  8. Only the base modifier can appear before a mixin declaration.

    The modifiers do not apply to other declarations like enum, typedef, or extension • abstract • base • final • interface • sealed • mixin Features
  9. /// ====>>>>>>> abstract classes ///To define a class that doesn’t

    require a full, concrete implementation of its ///entire interface, use the abstract modifier. ///Abstract classes cannot be constructed from any library, whether its own or ///an outside library. Abstract classes often have abstract methods. // Library a.dart abstract class Vehicle { void moveForward(int meters); } // Library b.dart import 'a.dart'; // Error: Cannot be constructed Vehicle myVehicle = Vehicle(); // Can be extended class Car extends Vehicle { double distance = 0.0; @override void moveForward(int meters) { // ... } }
  10. ///=======>>>>>>>>> base ///To enforce inheritance of a class or mixin’s

    implementation, ///use the base modifier. A base class disallows implementation ///outside of its own library. // Library a.dart base class Vehicle { void moveForward(int meters) { // ... } } // Library b.dart import 'a.dart'; // Can be constructed Vehicle myVehicle = Vehicle(); // Can be extended base class Car extends Vehicle { int passengers = 4; // ... } // ERROR: Cannot be implemented base class MockVehicle implements Vehicle { @override void moveForward() { // ... } }
  11. ///=======>>>>>>>> interface ///To define an interface, use the interface modifier.

    ///Libraries outside of the interface’s own defining library can ///implement the interface, but not extend it. // Library a.dart interface class Vehicle { void moveForward(int meters) { // ... } } // Library b.dart import 'a.dart'; // Can be constructed Vehicle myVehicle = Vehicle(); // ERROR: Cannot be inherited class Car extends Vehicle { int passengers = 4; // ... } // Can be implemented class MockVehicle implements Vehicle { @override void moveForward(int meters) { // ... } }
  12. ///===========>>>>>>sealed ///To create a known, enumerable set of subtypes, use

    the sealed modifier. /// This allows you to create a switch over those subtypes that is statically ///ensured to be exhaustive. sealed class Vehicle {} class Car extends Vehicle {} class Truck implements Vehicle {} class Bicycle extends Vehicle {} // ERROR: Cannot be instantiated Vehicle myVehicle = Vehicle(); // Subclasses can be instantiated Vehicle myCar = Car(); String getVehicleSound(Vehicle vehicle) { // ERROR: The switch is missing the Bicycle subtype or a default case. return switch (vehicle) { Car() => 'vroom', Truck() => 'VROOOOMM', }; }
  13. ///=======>>>>> final ///This prevents subtyping from a class outside of

    the current library. ///Disallowing both inheritance and implementation prevents subtyping entirely // Library a.dart final class Vehicle { void moveForward(int meters) { // ... } } // Library b.dart import 'a.dart'; // Can be constructed Vehicle myVehicle = Vehicle(); // ERROR: Cannot be inherited class Car extends Vehicle { int passengers = 4; // ... } class MockVehicle implements Vehicle { // ERROR: Cannot be implemented @override void moveForward(int meters) { // ... } }
  14. ///===========> mixins ///Mixins are a way of defining code that

    can be reused in multiple ///class hierarchies. They are intended to provide member implementations en masse. mixin Musical { bool canPlayPiano = false; bool canCompose = false; bool canConduct = false; void entertainMe() { if (canPlayPiano) { print('Playing piano'); } else if (canConduct) { print('Waving hands'); } else { print('Humming to self'); } } } To use a mixin, use the with keyword followed by one or more mixin names. class Musician extends Performer with Musical { // ··· entertainMe(); }
  15. You can combine some modifiers for layered restrictions. A class

    declaration can be, in order: 1. (Optional) abstract, describing whether the class can contain abstract members and prevents instantiation. 2. (Optional) One of base, interface, final or sealed, describing restrictions on other libraries subtyping the class. 3. (Optional) mixin, describing whether the declaration can be mixed in. 4. The class keyword itself. You can’t combine some modifiers because they are contradictory, redundant, or otherwise mutually exclusive: • abstract with sealed. A sealed class is always implicitly abstract. • interface, final or sealed with mixin. These access modifiers prevent mixing in. Combining modifiers Class modifiers
  16. “To build the most productive programming language for building fast

    apps on any platform” Dart long-term vision