Dart: Functions

Dart: Functions

Understand the functions in Dart.

A function is a block of code that performs a specific task. Functions allow us to split up our code into reusable parts.

Other common synonyms for function are:

  • Method

  • Procedure

  • Subroutine

  • Routine

The main roles of functions in code are:

  1. Modularity: Functions help organize our code into modular chunks that can be reused. This makes the code easier to read, maintain and debug.

  2. Reusability: We can define a function once and call it multiple times in our program. This avoids repetition of code.

  3. Easy debugging: Since functions have a limited scope, debugging a function is easier compared to debugging the entire code.

In this article, we will analyze the function as it is defined in Dart. What is its use and what different aspects are related to the usage of functions?


Dart Syntax

Before you can use a function you need to understand the basic syntax then the semantics and use-cases. Here is the syntax to define a function in Dart:

returnType functionName(parameters) { function body }

An example of a function in Dart:

int square(int number) {
  return number * number; 
}

void main() {
  int result = square(2);
  print(result); // 4
}

Here:

  • int is the return type

  • square() is the function name

  • number is the parameter

  • number * number is the function body that returns the square of the number

We defined the square() function that takes an integer and returns its square.

In the main() function, we called the square() function and passed 2 as argument. The result 4 was printed.


Function Anatomy

Parameters: Functions can take inputs in the form of parameters. The parameters act as variables that the function can use in its body. For example:

int sum(int a, int b) {
  // a and b are parameters
}

Here a and b are the parameters of the sum() function.

Computation body: The function body contains the code or computation that the function performs. For example:

int sum(int a, int b) {
  return a + b;  // Function body  
}

Here the function body simply adds the two parameters and returns the result.

Return value: Most functions return a result of the computation performed in the function body. The return type specifies what type of value the function returns. For example:

int sum(int a, int b) { ... }  // Return type is int

Function call: To execute a function, we call or invoke it by specifying the function name and passing arguments. For example:

int result = sum(10, 20);

Here sum() is the function call and 10 and 20 are the arguments passed.

Arguments: The values passed during a function call are known as arguments. The number of arguments passed must match the number of parameters defined in the function.


Scope Model

Dart has a lexical scope model. This means that the scope of variables is determined by the structure of the program's source code.

There are 4 scopes in Dart:

  1. Local scope: Variables declared within a function have local scope. They are only accessible within that function.

  2. Closure scope: Variables declared within a function and used within an inner function form a closure scope. The inner function has access to the variables of its outer function.

  3. Module scope: Variables declared at the top level of a Dart file have module scope. They are accessible within that file.

  4. Global scope: Variables declared within a library have global scope. They are accessible from any part of the library.

The scope hierarchy in Dart is:

Global > Module > Closure > Local

This means that:

  • A local variable cannot access a variable in an outer scope.

  • A module-scoped variable can access a global variable.

  • A closure-scoped variable can access variables in its module and global scopes.

  • A global variable can access all other scopes.

Some examples:

// Module scope
int num = 10; 

void main() {

  // Local scope  
  int num = 20;  

  print(num); // Prints 20  

}

print(num);  // Prints 10

Here num at the module level is shadowed by the local num variable. The local num hides the module-level num within its scope.

So in summary, Dart follows a lexical scope model where the structure of the source code determines the scope of variables. The scope hierarchy is Global > Module > Closure > Local.


Functional Programming

Functional programming is a paradigm where functions are first-class citizens. Some characteristics of functional programming in Dart are:

  1. Functions are treated as first-class citizens. This means functions can be:
  • Passed as arguments to other functions

  • Returned by other functions

  • Assigned to variables

For example:

void main() {
  Function sum = (a, b) => a + b;

  print(sum(2,3)); // Calls the function assigned to sum
}
  1. Simple tasks: Functional programming favors small, focused functions that perform one simple task.

  2. Immutability: Variables are preferably immutable. This makes reasoning about code easier.

  3. Avoiding side effects: Functions should not cause side effects by mutating external states. They should only take input and produce output.

  4. Higher-order functions: Functions that operate on other functions by taking them as arguments or returning them are called higher-order functions.

For example:

int operation(Function func, int a, int b) {
  return func(a, b); 
}

void main() {
  // Passes a function as argument
  print(operation((a, b) => a + b, 2, 3));  
}
  1. Map, filter and reduce collections Collection methods like map(), filter() and reduce() allow functional-style data transformations.

So in summary, Dart fully supports functional programming concepts like first-class functions, immutability, higher-order functions, etc. This makes Dart a versatile language that supports both OOP and FP paradigms.


Default values

Dart allows defining default values for function parameters. This means the parameters can have a default value which is used if no argument is passed for that parameter during the function call.

For example:

void greet(String name, {String title = 'Mr.'}) {
  print('Hello $title $name!');
}

Here the title parameter has a default value of 'Mr.'. So we can call the function as:

greet('John');  // Prints Hello Mr. John!

Since no title was passed, the default value 'Mr.' is used.

We can also pass an argument for title:

greet('John', title: 'Ms.');  
// Prints Hello Ms. John!

Here we explicitly passed 'Ms.' as the title, overriding the default value.

Some points about default parameters in Dart:

  • Default values are defined after required parameters

  • Default values are defined using =

  • Default values can be used for named parameters only

  • Default values are evaluated once when the function is defined

  • Default values cannot be constants, they have to be expressions

For example, this is invalid:

void greet(String name, {const String title = 'Mr.'}) // Invalid

So default parameter values allow functions to be called in different ways - by passing all arguments, passing some arguments and using defaults for others, or not passing any arguments and using all default values.


Variadic Functions

A variadic function is a function that can accept a variable number of arguments. This is achieved by defining one or more of the function's parameters with a ... (ellipsis) before the parameter name.

Such a parameter is called a variadic parameter and within the function body it acts like a list containing all the passed arguments of that type. Variadic functions allow for convenient handling of an indefinite number of arguments of a specific type.

For example:

void printNumbers(...int numbers) {
  for (int number in numbers) {
    print(number);    
  }
}

Here numbers is a vararg parameter of type int. We can call this function as:

printNumbers(1, 2, 3);
// 1 
// 2
// 3

printNumbers(4, 5, 6, 7);
// 4
// 5 
// 6
// 7

The numbers vararg parameter will contain a list of all the int arguments passed.

Some points about vararg parameters:

  • Vararg parameters must be the last parameters defined

  • They allow a function to take an indefinite number of arguments of a specific type

  • Inside the function, a vararg parameter is treated as a list

  • You access individual arguments using the [] operator

For example:

void printNumbers(...int numbers) {
  print(numbers[0]);
  print(numbers[1]); 
}

printNumbers(1, 2, 3);
// 1
// 2

So in summary, vararg parameters allow Dart functions to accept an indefinite number of arguments of a specific type. The vararg parameter acts as a list containing all the passed arguments.


Recursive Functions

A recursive function in Dart is a function that calls itself during its execution. This allows for a very elegant and concise way of representing certain algorithms.

Some key points about recursive functions in Dart:

  • A recursive function calls itself, either directly or indirectly. This results in multiple instances of the function being executed.

  • For a recursive function to terminate, there needs to be a base case - a condition when the function no longer calls itself.

  • Without a base case, a recursive function will continue calling itself indefinitely, resulting in a stack overflow error.

  • Dart imposes a limit on the maximum stack size to prevent infinite recursion from crashing the program.

  • Recursive functions are useful for representing algorithms that follow a recursive pattern, like factorial, Fibonacci series, tree traversals etc.

An example of a recursive function in Dart is calculating the factorial of a number:

int factorial(int n) {
  if (n == 1) {  
    // base case 
    return 1;
  } else {
    return n * factorial(n - 1);  
  }  
}

Here the base case is when n == 1, and in the recursive case the function calls factorial(n - 1) to calculate the factorial of the smaller number.

In summary, a recursive function is a function that calls itself, either directly or indirectly. It needs a base case to terminate, otherwise, it will result in a stack overflow error.


Disclaim: This article was created with Rix AI. There are so much more things to learn about functions. For advanced topics you are invited to visit my homepage:

sagecode.net