// 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<String Function(int)> 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>(T value) => value;
/// X Function<X>(X) anotherId = id; // Parameter name may be omitted.
/// int Function(int) intId = id<int>;
/// ```
///
/// 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<dynamic>? positionalArguments, [
    Map<Symbol, dynamic>? 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>(T value) => value;
  ///   assert(id<int> == id<int>);
  ///   ```
  ///
  ///   (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 = <Function>[];
  /// 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<int> 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);
}
