Dart Expressions

Elucian MoiseElucian Moise
9 min read

Expressions in Dart are constructs that evaluate to a single value. They are the most basic units of the Dart programming language.

The components of an expression in Dart are:

  1. Literals - These are values written directly in the code like string literals, numeric literals, boolean literals, list literals, map literals, etc.

  2. Variables - Variables store values and can be used in expressions.

  3. Operators - Operators are used to perform operations on values. Dart supports operators like + - * / % ++ -- etc.

  4. Function calls - Calling a function is also an expression as it evaluates to the return value of the function.

  5. Conditional expressions - The ?: operator can be used as an expression to evaluate one of two expressions based on a condition.

For example:

int a = 10;  // Literal 
String name = 'John'; // Variable
int b = a + 5; // Operator 
int result = multiply(a, b); // Function call
int age = isStudent ? 20 : 30;  // Conditional expression

Here a, name, b, result and age are all expressions as they evaluate a single value.

So in summary, expressions in Dart consist of literals, variables, operators, function calls and conditional expressions.

We have already defined the concept of a variable in our previous article, where we have also studied data types. Now we learn the rest of the components of an expression.


Data Literals

Data literals are a way to represent data values directly in the Dart code. They are used to initialize variables with fixed values.

The different types of data literals in Dart are:

  • Numerical literals: Represent integer and double values. For example:
int a = 10; 
double b = 10.5;
  • String literals: Represent string values. Use single ' or double " quotes. For example:
String name = 'John';
String greeting = "Hello";
  • Boolean literals: Represent true or false values. For example:
bool isTrue = true;
bool isFalse = false;
  • List literals: Represent lists of values. For example:
List names = ['John', 'Jane', 'Jack'];
  • Map literals: Represent key-value pairs. For example:
Map person = {
  'name': 'John',
  'age': 30
};
  • Symbol literals: Represent symbols. Use # syntax. For example:
var nameSymbol = #name;

Operators

Expressions in Dart have a specific type, depending on the literals, variables and functions used in the expression. The type of an expression determines which operators can be used on that expression.

For example:

  • An expression with numeric literals and variables has a numeric type (int or double) and can only use numeric operators like + - * / % etc.
int a = 10; 
double b = 20.5;
int result = a + b; // OK, + is a numeric operator
String str = a + b; // Error, cannot use + on non-numeric types
  • An expression with string literals and variables has a String type and can only use string operators like + (concatenation), == etc.
String name = 'John';
String greeting = 'Hello ' + name; // OK, + is string concatenation
int len = name + 1; // Error, cannot use + to concat string and int
  • An expression with boolean literals has bool type and can only use boolean operators like && || ! etc.
bool isTrue = true && false;
int result = isTrue || 10; // Error, || cannot be used on bool and int

So in summary, the type of an expression determines:

  • Which literals, variables and functions can be used in that expression

  • Which operators can be applied on that expression

This allows Dart to check for type safety and catch type mismatches at compile time.


Priority

Operator priority determines the order in which operators are evaluated in an expression. Operators with higher priority are evaluated first.

In Dart, operators have the following priority (from highest to lowest):

  1. Parentheses ()

  2. Unary operators: ! ++ --

  3. Multiplication * / %

  4. Addition - +

  5. Relational operators: < <= > >=

  6. Equality operators: == !=

  7. Bitwise operators: & ^ |

  8. Logical AND &&

  9. Logical OR ||

For example:

int result = 10 + 5 * 2;  // Evaluates to 20, * has higher priority than +

result = (10 + 5) * 2; // Evaluates to 30, () has highest priority

bool isTrue = false || false && true; // Evaluates to false, || has higher priority

You can change the evaluation order by using parentheses to group expressions with lower-priority operators first.

So in summary, operator priority determines the order of evaluation in Dart expressions. Operators with higher priority are applied first, from left to right.


Long Expressions

You're right, long expressions in Dart can be created by combining multiple short expressions enclosed in parentheses. This has some advantages and disadvantages:

Advantages:

  • Faster execution since there are no intermediate variables. The entire expression is evaluated in one go.

  • More concise code with fewer variables.

Disadvantages:

  • Harder to debug since you cannot inspect intermediate values.

  • Less readable since the entire expression is on one line.

An example:

Instead of:

var a = 10;
var b = 20; 
var c = a + b;
print(c);  // 30

You can write:

print((10 + 20)); // 30

By enclosing the short expressions in parentheses, we can combine them into a single long-expression.

This long-expression will be:

  • Faster since there are no intermediate variables a and b

  • More concise with fewer lines of code

  • Harder to debug since we cannot inspect the values of a and b

  • Less readable since it's a long expression on one line

So in summary, long expressions in Dart created from combining short expressions in parentheses have:

  • Faster execution

  • More concise code

  • But are harder to debug and less readable

Use long expressions when performance matters and short, readable expressions with intermediate variables when readability and debuggability are important.


Semicolon

The semicolon ";" in Dart acts as a statement terminator. It indicates the end of a statement.

This has some implications for long expressions in Dart:

  • Semicolons are optional in Dart. If a line has only an expression and no semicolon, the semicolon is automatically inserted at the end.

  • For long expressions spanning multiple lines, semicolons are required to terminate each line and indicate where one expression ends and the next begins.

For example:

int result = 10 + 20;  // Semicolon is optional

int result = 10 +  
             20 +
             30;  // Semicolon required to terminate each line

int result = (10 + 20) *  
              (30 + 40); // Semicolons terminate each expression

Without semicolons, Dart would not know where one expression ends and the next begins.

So in summary:

  • Semicolons are optional in Dart for single-line expressions

  • For long expressions spanning multiple lines, semicolons are required to terminate each line and indicate where one expression ends and the next begins.

This allows Dart to correctly parse long expressions consisting of multiple statements.

Backslash

You can use the \ (backslash) character to continue an expression onto the next line in Dart.

For example:

int result =   10 \
             + 20 \
             + 30;

Is equivalent to:

int result = 10 + 
             20 + 
             30;

The \ character indicates that the expression continues on the next line.

So instead of using semicolons (;) to terminate each line in a long expression, you can use the backslash () to continue the expression onto the next line.

This can make long expressions more readable by breaking them into multiple lines.

So in summary, you have two options for long expressions in Dart:

  1. Use semicolons (;) at the end of each line

  2. Use a backslash (\) to continue the expression onto the next line

Both approaches are equivalent and achieve the same result. Use whichever style you prefer for readability.


Use-Cases

Expressions in Dart (and in most programming languages) have many use cases:

  1. Assigning values to variables:
var a = 10;   // Simple expression 
var b = a + 20; // Expression using another variable

Here the expressions 10 and a + 20 are used to assign values to the variables a and b.

  1. Passing arguments to functions:
print(a + b); // Expression passed as argument

Here the expression a + b is passed as an argument to the print() function.

  1. Conditional expressions:
var result = a > b ? 'a is greater' : 'b is greater';

The conditional (?:) operator is an expression that evaluates to one of two values based on a condition.

  1. Returning values from functions:
int sum(int a, int b) {
  return a + b;  // Expression returned  
}

The expression a + b is returned from the function.

  1. Creating objects:
var point = Point(x: 5, y: 10);  // Expression creates an object

The expression Point(x: 5, y: 10) creates a Point object.

  1. Performing calculations:
var area = width * height;  // Expression performs calculation

The expression width * height calculates the area.

So in summary, expressions in Dart are used in many places:

  • Assigning values

  • Passing arguments

  • Conditional expressions

  • Returning values

  • Creating objects

  • Performing calculations


Efficiency

When you create expressions you must be aware of the cost of each expression. Is a good practice to avoid repeated evaluation of the same expression, especially in loops. You should avoid very complex expressions that are harder to optimize.

To create optimized and efficient expressions, there are some more advanced concepts and techniques to consider. We will explain these concepts later after we learn the syntax of Dart in detail. Here are some concepts that require more study:

  1. Operator precedence - Understanding the order in which Dart evaluates operators can help you write more optimized expressions. For example, multiplying before adding in an expression can be more efficient.

  2. Constant expressions - Expressions that can be fully evaluated at compile time are called constant expressions. These are the most optimized expressions as they don't require any runtime evaluation.

  3. Inline expansion - The Dart compiler can inline the evaluation of small functions into the calling expression to make it more efficient. Understanding when and how this happens can help you optimize your code.

  4. Strength reduction - The Dart compiler can apply optimizations like common subexpression elimination, constant folding, etc. on your expressions to make them more efficient.

  5. Intrinsic functions - Dart provides intrinsic functions for some operations that are mapped directly to CPU instructions. Using these can make your expressions highly optimized.

  6. Bit-level operations - Performing operations at the bit level (like bit shifting, masking, etc.) can optimize some expressions significantly.

  7. Specializing functions - Writing specialized versions of functions for specific argument types can allow the Dart compiler to inline and optimize the expressions that call those functions.

So in summary, these knowledge will allow you to continue study and understand example in our next articles. Optimizing expressions to achieve maximum performance are advanced concepts that we will explain when you are better prepared to understand more complex code.


Disclaim: This series of articles is created with Rix AI bot. If you find errors, please comment below. I will correct any error signaled.

0
Subscribe to my newsletter

Read articles from Elucian Moise directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Elucian Moise
Elucian Moise

Software engineer instructor, software developer and community leader. Computer enthusiast and experienced programmer. Born in Romania, living in US.