// Copyright (c) 2011, 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 function value. /// /// The `Function` class is a supertype of all *function types*, and contains /// no values itself. All objects that implement `Function` /// have a function type as their runtime type. /// /// The `Function` type does not carry information about the /// parameter signatures or return type of a function. /// To express a more precise function type, use the function type syntax, /// which is the `Function` keyword followed by a parameter list, /// or a type argument list and a parameter list, and which can also have /// an optional return type. /// /// The function type syntax mirrors the definition of a function, /// with the function name replaced by the word "Function". /// /// Example: /// ```dart /// String numberToString(int n) => "$n"; /// String Function(int n) fun = numberToString; // Type annotation /// assert(fun is String Function(int)); // Type check. /// List functions = [fun]; // Type argument. /// ``` /// The type `String Function(int)` is the type of a function /// that takes one positional `int` argument and returns a `String`. /// /// Example with generic function type: /// ```dart /// T id(T value) => value; /// X Function(X) anotherId = id; // Parameter name may be omitted. /// int Function(int) intId = id; /// ``` /// /// A function type can be used anywhere a type is allowed, /// and is often used for functions taking other functions, "callbacks", /// as arguments. /// /// ```dart /// void doSomething(String Function(int) callback) { /// print(callback(1)); /// } /// ``` /// /// A function type has all the members declared by [Object], /// since function types are subtypes of [Object]. /// /// A function type also has a `call` method with a signature /// that has the same function type as the function type itself. /// Calling the `call` method behaves just as calling the function. /// This is mainly used to conditionally call a nullable function value. /// ```dart /// String Function(int) fun = (n) => "$n"; /// String Function(int) fun2 = fun.call; // Valid. /// print(fun2.call(1)); // Prints "1". /// /// String Function(int)? maybeFun = Random().nextBool() ? fun : null; /// print(maybeFun?.call(1)); // Prints "1" or "null". /// ``` /// /// The [Function] type has a number of special features which are not visible /// in this `class` declaration. /// /// The `Function` type itself allows any function to be assigned to it, /// since it is a supertype of any function type, /// but does not say how the function can be called. /// /// However, a value with the static type `Function` *can* still be called /// like a function. /// ```dart /// Function f = (int x) => "$x"; /// print(f(1)); // Prints "1". /// /// f("not", "one", "int"); // Throws! No static warning. /// ``` /// Such an invocation is a *dynamic* invocation, /// precisely as if the function value had been statically typed as [dynamic], /// and is precisely as unsafe as any other dynamic invocation. /// Checks will be performed at run-time to ensure that the argument /// list matches the function's parameters, and if not the call will /// fail with an [Error]. /// There is no static type checking for such a call, any argument list /// is accepted and checked at runtime. /// /// Like every function type has a `call` method with its own function type, /// the `Function` type has a special `call` member /// which acts as if it is a method with a function type of `Function` /// (which is not a method signature which can be expressed in normal /// Dart code). /// ```dart /// Function fun = (int x) => "$x"; /// /// var fun2 = fun.call; // Inferred type of `fun2` is `Function`. /// /// print(fun2.call(1)); // Prints "1"; /// /// Function? maybeFun = Random().nextBool() ? fun : null; /// print(maybeFun?.call(1)); // Prints "1" or "null". /// ``` abstract final class Function { /// Dynamically call [function] with the specified arguments. /// /// Acts the same as dynamically calling [function] with /// positional arguments corresponding to the elements of [positionalArguments] /// and named arguments corresponding to the elements of [namedArguments]. /// /// This includes giving the same errors if [function] /// expects different parameters. /// /// Example: /// ```dart /// void printWineDetails(int vintage, {String? country, String? name}) { /// print('Name: $name, Country: $country, Vintage: $vintage'); /// } /// /// void main() { /// Function.apply( /// printWineDetails, [2018], {#country: 'USA', #name: 'Dominus Estate'}); /// } /// /// // Output of the example is: /// // Name: Dominus Estate, Country: USA, Vintage: 2018 /// ``` /// /// If [positionalArguments] is null, it's considered an empty list. /// If [namedArguments] is omitted or null, it is considered an empty map. /// /// ```dart /// void helloWorld() { /// print('Hello world!'); /// } /// /// void main() { /// Function.apply(helloWorld, null); /// } /// // Output of the example is: /// // Hello world! /// ``` external static apply( Function function, List? positionalArguments, [ Map? namedArguments, ]); /// A hash code value that is compatible with `operator==`. int get hashCode; /// Test whether another object is equal to this function. /// /// Function objects are only equal to other function objects (an object /// satisfying `object is Function`), and never to non-function objects. /// /// Some function objects are considered equal by `==` because they are /// recognized as representing the "same function": /// /// - It is the same object. Static and top-level functions are compile time /// constants when used as values, so referring to the same function twice /// always yields the same object, as does referring to a local function /// declaration twice in the same scope where it was declared. /// /// ```dart /// void main() { /// assert(identical(print, print)); /// int add(int x, int y) => x + y; /// assert(identical(add, add)); /// } /// ``` /// /// - The functions are same member method extracted from the same object. /// Repeatedly extracting ("tearing off") the same instance method of the /// same object to a function value gives equal, but not necessarily /// identical, function values. /// /// ```dart /// var o = Object(); /// assert(o.toString == o.toString); /// ``` /// /// - Instantiations of equal generic functions with the *same* types /// yields equal results. /// /// ```dart /// T id(T value) => value; /// assert(id == id); /// ``` /// /// (If the function is a constant and the type arguments are known at /// compile-time, the results may also be identical.) /// /// Different evaluations of function literals are not guaranteed or required /// to give rise to identical or equal function objects. For example: /// /// ```dart /// var functions = []; /// for (var i = 0; i < 2; i++) { /// functions.add((x) => x); /// } /// print(identical(functions[0], functions[1])); // 'true' or 'false' /// print(functions[0] == functions[1]); // 'true' or 'false' /// ``` /// /// If the distinct values are identical, they are always equal. /// /// If the function values are equal, they are guaranteed to behave /// indistinguishably for all arguments. /// /// If two functions values behave differently, they are never equal or /// identical. /// /// The reason to not require a specific equality or identity of the values /// of a function expression is to allow compiler optimizations. If a /// function expression does not depend on surrounding variables, an /// implementation can safely be shared between multiple evaluations. For /// example: /// /// ```dart /// List ints = [6, 2, 5, 1, 4, 3]; /// ints.sort((x, y) => x - y); /// print(ints); /// ``` /// /// A compiler can convert the closure `(x, y) => x - y` into a top-level /// function. bool operator ==(Object other); }