| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of "dart:core"; |
| |
| /// A record value. |
| /// |
| /// The `Record` class is a supertype of all *record types*, |
| /// but is not itself the runtime type of any object instances |
| /// _(it's an abstract class)_. |
| /// All objects that implement `Record` has a record type as their runtime type. |
| /// |
| /// A record value, described by a record type, consists of a number of fields, |
| /// which are each either positional or named. |
| /// |
| /// Record values and record types are written similarly to |
| /// argument lists and simplified function type parameter lists (no `required` |
| /// modifier allowed, or needed, since record fields are never optional). |
| /// Example: |
| /// ```dart |
| /// (int, String, {bool isValid}) triple = (1, "one", isValid: true); |
| /// ``` |
| /// is syntactically similar to |
| /// ```dart |
| /// typedef F = void Function(int, String, {bool isValid}); |
| /// void callIt(F f) => f(1, "one", isValid: true); |
| /// ``` |
| /// |
| /// Every record and record type has a *shape*, |
| /// given by the number of positional fields and the names of named fields. |
| /// For example: |
| /// ```dart continued |
| /// (double value, String name, {String isValid}) another = ( |
| /// 3.14, "Pi", isValid: "real"); |
| /// ``` |
| /// is another record declaration with the same *shape* (two positional fields, |
| /// one named field named `isValid`), but with a different type. |
| /// The names written on the positional fields are entirely for documentation |
| /// purposes, they have no effect on the program _(same as names on positional |
| /// parameters in function types, like `typedef F = int Function(int value);`, |
| /// where the identifier `value` has no effect)_. |
| /// |
| /// Record values are mainly destructured using patterns, like: |
| /// ```dart continued |
| /// switch (triple) { |
| /// case (int value, String name, isValid: bool ok): // .... |
| /// } |
| /// ``` |
| /// The individual fields can also be accessed using named getters, |
| /// using `$1`, `$2`, etc. for positional fields, and the names themselves |
| /// for named fields. |
| /// ```dart continued |
| /// int value = triple.$1; |
| /// String name = triple.$2; |
| /// bool ok = triple.isValid; |
| /// ``` |
| /// Because of that, some identifiers cannot be used as names of named fields: |
| /// * The names of `Object` members: `hashCode`, `runtimeType`, `toString` and |
| /// `noSuchMethod`. |
| /// * The name of a positional getter in the same record, so `(0, $1: 0)` is |
| /// invalid, but `(0, $2: 0)` is valid, since there is no positional field |
| /// with getter `$2` in *that* record shape. _(It'll still be confusing, |
| /// and should be avoided in practice.)_ |
| /// * Also, no name starting with an underscore, `_`, is allowed. Field names |
| /// cannot be library private. |
| /// |
| /// The run-time type of a record object is a record type, and as such, a |
| /// subtype of [Record], and transitively of [Object] and its supertypes. |
| /// |
| /// Record values do not have a persistent [identical] behavior. |
| /// A reference to a record object can change *at any time* to a reference |
| /// to another record object with the same shape and field values. |
| /// |
| /// Other than that, a record type can only be a subtype of another record |
| /// type with the same shape, and only if the former record type's field types |
| /// are subtypes of the other record type's corresponding field types. |
| /// That is, `(int, String, {bool isValid})` is a subtype of |
| /// `(num, String, {Object isValid})`, because they have the same shape, |
| /// and the field types are pointwise subtypes. |
| /// Record types with different shapes are unrelated to each other. |
| abstract final class Record { |
| /// A `Type` object representing the runtime type of a record. |
| /// |
| /// The runtime type of a record is defined by the record's *shape*, |
| /// the number of positional fields and names of named fields, |
| /// and the runtime type of each of those fields. |
| /// (The runtime type of the record does not depend on |
| /// the `runtimeType` getter of its fields' values, |
| /// which may have overridden [Object.runtimeType].) |
| /// |
| /// The `Type` object of a record type is only equal to another `Type` object |
| /// for a record type, and only if the other record type has the same shape, |
| /// and if the corresponding fields have the same types. |
| Type get runtimeType; |
| |
| /// A hash-code compatible with `==`. |
| /// |
| /// Since [operator==] is defined in terms of the `==` operators of |
| /// the record's field values, the hash code is also computed based on the |
| /// [Object.hashCode] of the field values. |
| /// |
| /// There is no guaranteed order in which the `hashCode` of field values |
| /// is accessed. |
| /// It's unspecified how those values are combined, |
| /// other than it being consistent throughout a single program execution. |
| int get hashCode; |
| |
| /// Checks whether [other] has the same shape and equal fields to this record. |
| /// |
| /// A record is only equal to another record with the same *shape*, |
| /// and then only when the value of every field is equal, |
| /// occording to its `==`, to the corresponding field value of [other]. |
| /// |
| /// There is no guaranteed order in which field value equality is checked, |
| /// and it's unspecified whether further fields are checked after finding |
| /// corresponding fields which are not equal. |
| /// It's not even guaranteed that the order is consistent within a single |
| /// program execution. |
| /// |
| /// As usual, be very careful around objects which break the equality |
| /// contract, like [double.nan] which is not equal to itself. |
| /// For example |
| /// ```dart |
| /// var pair = ("a", double.nan); |
| /// if (pair != pair) print("Oops"); |
| /// ``` |
| /// will print the "Oops", because `pair == pair` is defined to be equal to |
| /// `"a" == "a" & double.nan == double.nan`, which is false. |
| bool operator ==(Object other); |
| |
| /// Creates a string-representation of the record. |
| /// |
| /// The string representation is only intended for debugging, |
| /// and may differ between development and production. |
| /// There is no guaranteed format in production mode. |
| /// |
| /// In development mode, the string will strive to be a parenthesized |
| /// comma separated list of field representations, where the field |
| /// representation is the `toString` of the value for positional fields, |
| /// and `someName:` followed by that for a named field named `someName`. |
| String toString(); |
| } |