Book Image

Mastering Dart

By : Sergey Akopkokhyants
Book Image

Mastering Dart

By: Sergey Akopkokhyants

Overview of this book

Table of Contents (19 chapters)
Mastering Dart
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Functions and closures in different scopes


I like Dart because everything is an object. Functions are first-class citizens because they support all the operations that are generally available to other types. This means each function have the following properties:

  • They can be named by a variable

  • They can be passed as an argument to a function

  • They can be returned as the result of a function

  • They can be stored in data structures

  • They can be created in any scope

Let's see where and how can we use functions as usual or as first-class citizens.

Naming functions with a variable

Naming functions by variable means that we can create a reference to a function and assign it to a variable, as shown in the following code:

library function_var;

// Returns sum of [a] and [b]
add(a, b) {
  return a + b;
}

// Operation
var operation;

void main() {
  // Assign reference to function [add]
  operation = add;
  // Execute operation
  var result = operation(2, 1);
  print("Result is ${result}");}");
}

Here is the result of the preceding code:

Result is 3

We have the add function and the operation variable. We assign the reference of the add function to a variable and call the variable as a function later.

Passing a function as an argument to another function

Passing functions as arguments to other functions can be very useful in cases when we need to implement the strategy design pattern to enable the program code to be selected and executed at runtime, as shown in the following code:

library function_param;

// Returns sum of [a] and [b]
add(a, b) {
  return a + b;
}

// Operation executor
executor(operation, x, y) {
  return operation(x, y);
}

void main() {
  // Execute operation
  var result = executor(add, 2, 1);
  print("Result is ${result}");
}

Here is the result of the preceding code:

Result is 3

The global executor function from the preceding example can call any function that accepts two arguments. You can see the implementation of the strategy design pattern in the form of anonymous functions passed as parameters of methods in collections.

Returning a function as a result of another function

Sometimes, a function can be returned as a result of another function, as shown in the following code:

library function_return;

// Returns sum of [a] and [b]
add(a, b) => a + b;

// Returns difference between [a] and [b]
sub(a, b) => a - b;

// Choose the function depends on [type]
chooser(bool operation) =>operation ? add : sub;

void main() {
  // Choose function depends on operation type
  var operation = chooser(true);
  // Execute it
  var result = operation(2, 1);
  // Result
  print("Result is ${result}");
}

Here is the result of the preceding code:

Result is 3

This option can be very useful in implementing closures.

Storing a function in data structures

We can store a function in data structures in any collection, as shown in the following code:

library function_store;

// Returns sum of [a] and [b]
add(a, b) => a + b;

// Returns difference between [a] and [b]
sub(a, b) => a - b;

// Choose the function depends on [type]
var operations = [add, sub];

void main() {
  // Choose function from list
  var operation = operations[0];
  // Execute it
  var result = operation(2, 1);
  // Result
  print("Result is ${result}");
}

Here is the result of the preceding code:

Result is 3

We have two functions and the array operations in our example that stores references to them.

Closures

A function can be created in the global scope or within the scope of another function. A function that can be referenced with an access to the variables in its lexical scope is called a closure, as shown in the following code:

library function_closure;

// Function returns closure function.
calculate(base) {
  // Counter store
  var count = 1;
  // Inner function - closure
  return () => print("Value is ${base + count++}");
}

void main() {
  // The outer function returns inner
  var f = calculate(2);
  // Now we call closure
  f();
  f();
}

Here is the result of the preceding code:

Value is 3
Value is 4

We have the calculate function, which contains the count variable and returns a an inner function. The inner function has an access to the count variable because both are defined in the same scope. The count variable exists only within the scope of calculate and would normally disappear when the function exits. This does not happen in this case because the inner function returned by calculate holds a reference to count. The variable has been closed covered, meaning it's within a closure.

Finally, we know what a first-class function is, where we can use them, and how important it is to use closures. Let's move ahead to classes and mixins.