Watch the video (17 min)
On this page29 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. Operators That Work on One Value
  5. 2. The Increment Operator ++
  6. 3. The Decrement Operator --
  7. 4. Prefix and Postfix
  8. 5. The Value an Increment Gives Back
  9. 6. Tracing Prefix and Postfix Step by Step
  10. 7. Where ++ and -- Cannot Be Used
  11. 8. The Conditional Expression ? :
  12. 9. It Is an Expression, So It Fits Anywhere
  13. 10. Both Sides Should Have the Same Type
  14. 11. Conditional Expression or ???
  15. 12. Building Text with a StringBuffer
  16. 13. The Cascade Operator ..
  17. 14. A Cascade Gives Back the Object, Not the Result
  18. 15. Creating and Filling an Object in One Step
  19. 16. The Null-Aware Cascade ?..
  20. 17. A Real-World Example
  21. Code Examples
  22. Common Beginner Mistakes
  23. Best Practices
  24. Mini Challenge
  25. Practice Questions
  26. Practice Answers
  27. Quick Revision
  28. Key Takeaways
  29. What's Next?

Lecture 20 — Increment, Conditional & Cascade Operators

Category4 — Operators
Lecture020 of 140
Estimated video length17 min
TargetDart 3.12.2
LevelBeginner

Adding one, choosing between two values, and calling several methods on the same object are things programs do constantly. Dart has a short operator for each: ++, ? :, and ...

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 001–019:

  • variables with var, final, and explicit types
  • int, double, String, and bool, and interpolation with $ and ${...}
  • nullable types (int?) and the null-aware operators ?. and ??
  • arithmetic and compound assignment such as += and -=
  • comparison operators (==, >=, <) and logical operators (&&, ||, !)

Connection With Previous Lectures

Look at three lines a real program might contain:

dart
visitors += 1;
var label = '';
buffer.write('Ali');

Each one hides a problem. visitors += 1 is the most common update in programming, yet we still spell out the 1 every time. label needs to be 'Pass' or 'Fail' depending on a mark — and with the operators we know so far, we can calculate mark >= 50, but we cannot turn that bool into one of two words. And if we call write ten times on the same object, we type its name ten times. This lecture gives each problem its own operator.


Learning Objectives

By the end of this lecture you will be able to:

  • Recognize unary operators, which work on a single value
  • Use ++ and -- to add or subtract one
  • Explain the difference between prefix (++a) and postfix (a++) forms
  • Choose between two values with the conditional expression condition ? a : b
  • Use a conditional expression inside assignments and string interpolation
  • Build text step by step with a StringBuffer
  • Call several methods on one object with the cascade operator ..
  • Use the null-aware cascade ?.. on a value that might be null

1. Operators That Work on One Value

Most operators you have met sit between two values: price * quantity, mark >= 50. A unary operator works on just one value. You already know two of them:

dart
void main() {
  var balance = 250;
  var isOpen = false;
  print(-balance);
  print(!isOpen);
}
output
-250
true

-balance gives the negative of balance, and !isOpen flips a bool. Neither one changes the variable — balance is still 250 afterwards. The operators in the next sections are different: they change the variable itself.


2. The Increment Operator ++

++ adds one to a variable and stores the result back in it.

Analogy — think of a tally counter, the little clicker a doorman holds. Each click adds exactly one to the number on the display. ++ is one click.

dart
void main() {
  var visitors = 0;
  visitors++;
  visitors++;
  visitors++;
  print(visitors);
}
output
3

visitors++ means exactly the same as visitors += 1 or visitors = visitors + 1 — it is simply the shortest way to write it.


3. The Decrement Operator --

-- is the opposite: it subtracts one and stores the result back.

dart
void main() {
  var ticketsLeft = 5;
  ticketsLeft--;
  ticketsLeft--;
  print(ticketsLeft);
}
output
3

Both operators work on double variables too. They still change the value by exactly one:

dart
void main() {
  var temperature = 25.5;
  temperature++;
  print(temperature);
  temperature--;
  temperature--;
  print(temperature);
}
output
26.5
24.5

4. Prefix and Postfix

You can write the operator after the variable (score++, called postfix) or before it (++score, called prefix). On a line of its own, there is no difference at all:

dart
void main() {
  var a = 10;
  var b = 10;
  a++;
  ++b;
  print(a);
  print(b);
}
output
11
11

The difference appears only when you use the result of the operation in the same expression — for example inside print().


5. The Value an Increment Gives Back

An increment does two jobs: it changes the variable, and it produces a value, like any other expression. The position of ++ decides which value you get:

  • Postfix a++ — gives back the old value, then adds one.
  • Prefix ++a — adds one first, then gives back the new value.
dart
void main() {
  var a = 5;
  print(a++);
  print(a);

  var b = 5;
  print(++b);
  print(b);
}
output
5
6
6
6

print(a++) printed 5, the old value — but a had still become 6, as the next line shows. print(++b) added one first, so it printed 6 straight away. A simple way to remember it: read from left to right. In a++ you meet the variable first, so you get it before the change; in ++a you meet the ++ first, so the change happens first.


6. Tracing Prefix and Postfix Step by Step

When the result is stored in another variable, trace the lines one at a time:

dart
void main() {
  var count = 1;
  var first = count++; // first gets 1, then count becomes 2
  var second = ++count; // count becomes 3, then second gets 3
  print('first: $first, second: $second, count: $count');
}
output
first: 1, second: 3, count: 3

This works, but it makes the reader stop and think. In real code, put ++ and -- on a line of their own whenever you can, so the prefix/postfix question never comes up.


7. Where ++ and -- Cannot Be Used

The operator stores a new value, so it needs a variable that can be changed and that holds a number. A final variable cannot be changed:

dart
void main() {
  final count = 1;
  count++; // ✗
}
output
Error: Can't assign to the final variable 'count'.
  count++; // ✗
  ^^^^^

A nullable number is rejected as well, because adding one to null makes no sense:

dart
void main() {
  int? score;
  score++; // ✗
}
output
Error: Operator '+' cannot be called on 'int?' because it is potentially null.
  score++; // ✗
       ^
Error: A value of type 'num' can't be assigned to a variable of type 'int?'.
  score++; // ✗
       ^

Notice that both messages talk about adding and assigning: Dart treats score++ as score = score + 1, and it refuses that + on a value that might be null.


8. The Conditional Expression ? :

A conditional expression chooses one of two values based on a bool:

output
condition ? valueIfTrue : valueIfFalse
dart
void main() {
  var mark = 72;
  var result = mark >= 50 ? 'Pass' : 'Fail';
  print(result);
}
output
Pass

Read it as a question: "Is mark >= 50? If yes, 'Pass'; otherwise, 'Fail'." Because it uses three parts, many programmers call it the ternary operator. The condition can be any bool expression — including the comparison and logical operators from Lectures 018 and 019.


9. It Is an Expression, So It Fits Anywhere

A conditional expression produces a value, so you can put it wherever a value is expected — for example straight inside interpolation with ${...}:

dart
void main() {
  var items = 1;
  var balance = -40;
  print('You have $items ${items == 1 ? 'item' : 'items'}.');
  print('Account: ${balance < 0 ? 'overdrawn' : 'in credit'}');
}
output
You have 1 item.
Account: overdrawn

Only the chosen side is ever worked out; the other side is skipped completely. Choosing between whole blocks of statements — not just two values — is the job of if, which starts in Lecture 022. Lecture 023 returns to conditional expressions and compares the two.


10. Both Sides Should Have the Same Type

The variable that receives the result gets the type that fits both sides. When both sides are String, the result is a String. Mixing types leads to a wider type:

dart
void main() {
  var points = 120;
  var isMember = points >= 100;
  var mixed = isMember ? 10 : 2.5;
  var decimals = isMember ? 10.0 : 2.5;
  print(mixed);
  print(decimals);
}
output
10
10.0

mixed is a num, because one side is an int and the other a double — so it holds the int 10 here, and would hold the double 2.5 for a non-member. decimals is always a double, because both sides are. When a value is money or a measurement, give both sides the same type.


11. Conditional Expression or ???

In Lecture 014 you used ?? to replace a missing value. For that one job, ?? is a shorter conditional expression:

dart
void main() {
  var input = '';
  String? nickname = input.isEmpty ? null : input;
  var shown1 = nickname != null ? nickname : 'Guest';
  var shown2 = nickname ?? 'Guest';
  print(shown1);
  print(shown2);
}
output
Guest
Guest

Both lines give the same answer. Use ?? when the question is "is it null?"; use ? : for every other question, such as mark >= 50.


12. Building Text with a StringBuffer

Before the cascade operator, we need an object worth calling methods on. A StringBuffer collects text piece by piece, and gives you the finished String at the end:

dart
void main() {
  var receipt = StringBuffer();
  receipt.write('Notebook');
  receipt.write(' x 2');
  receipt.writeln();
  receipt.write('Total: 1000');
  print(receipt.toString());
}
output
Notebook x 2
Total: 1000

StringBuffer() creates a new, empty buffer. write adds text, writeln adds text followed by a line break, and toString() returns everything collected so far. Unlike a String, which never changes (Lecture 008), a buffer is changed by each write. The parentheses in StringBuffer() create a new object; how that works is the topic of Lecture 053, Creating Classes & Objects.


13. The Cascade Operator ..

Notice how often receipt. was repeated above. The cascade operator .. lets you perform several operations on the same object, writing its name only once:

dart
void main() {
  var receipt = StringBuffer();
  receipt
    ..write('Notebook')
    ..write(' x 2')
    ..writeln()
    ..write('Total: 1000');
  print(receipt);
}
output
Notebook x 2
Total: 1000

Each .. means "and on that same object, also do this". Dart formats a cascade with one operation per line, which reads like a checklist. print(receipt) works without toString(), because print converts the buffer to text by itself.


14. A Cascade Gives Back the Object, Not the Result

This is the most important rule about cascades. A normal method call x.method() gives back the method's result. A cascade x..method() throws that result away and gives back x itself:

dart
void main() {
  var word = 'hello';
  var normal = word.toUpperCase();
  var cascade = word..toUpperCase();
  print(normal);
  print(cascade);
}
output
HELLO
hello

word.toUpperCase() returned a new string, 'HELLO'. With .. the new string was created and then discarded, and the cascade handed back word — still 'hello'. Cascades are therefore useful for methods that change an object, like write, not for methods that return a new value.


15. Creating and Filling an Object in One Step

Because a cascade gives back the object, you can create a buffer, fill it, and store it — all in one statement:

dart
void main() {
  var greeting = StringBuffer()
    ..write('Hello, ')
    ..write('Sara')
    ..write('!');
  print(greeting);
  print(greeting.length);
}
output
Hello, Sara!
12

StringBuffer() creates the buffer, the three cascades fill it, and the whole expression's value — the buffer itself — goes into greeting. length counts the characters collected so far. Cascades become even more useful with lists (Lecture 039) and with your own classes (Lecture 053), where you will also use them to set properties.


16. The Null-Aware Cascade ?..

If the object might be null, a plain .. would be rejected, just as a plain . is. Start the cascade with ?.. instead. If the object is null, the whole cascade is skipped; otherwise it runs normally:

dart
void main() {
  var answer = 'no';
  var wantsNotes = answer == 'yes';
  StringBuffer? notes = wantsNotes ? StringBuffer() : null;
  notes
    ?..write('Bring ID')
    ..write(' and a pen');
  print(notes);
}
output
null

notes is null, so neither write ran and nothing crashed. Write ?.. only on the first operation; if the object is null, the following .. operations are skipped too. It is the cascade version of the ?. operator from Lecture 014.


17. A Real-World Example

dart
void main() {
  var customer = 'Ali';
  var price = 400;
  var quantity = 2;
  var itemsScanned = 0;

  itemsScanned++;
  itemsScanned++;
  var total = price * quantity;
  var delivery = total >= 500 ? 0 : 150;

  var receipt = StringBuffer()
    ..writeln('Customer: $customer')
    ..writeln('Items scanned: $itemsScanned')
    ..writeln('Total: $total')
    ..write('Delivery: ${delivery == 0 ? 'free' : delivery}');
  print(receipt);
}
output
Customer: Ali
Items scanned: 2
Total: 800
Delivery: free

++ counts the items, a conditional expression decides the delivery charge (and how to show it), and a cascade builds the receipt in one statement.


Code Examples

Example 1 — Counting Up

dart
void main() {
  var likes = 10;
  likes++;
  likes++;
  print('Likes: $likes');
}
output
Likes: 12

Example 2 — Counting Down

dart
void main() {
  var lives = 3;
  lives--;
  print('Lives left: $lives');
  lives--;
  print('Lives left: $lives');
}
output
Lives left: 2
Lives left: 1

Example 3 — Prefix vs Postfix Side by Side

dart
void main() {
  var x = 7;
  var y = 7;
  print('x++ gives ${x++}, x is now $x');
  print('++y gives ${++y}, y is now $y');
}
output
x++ gives 7, x is now 8
++y gives 8, y is now 8

Example 4 — Choosing a Word

dart
void main() {
  var age = 15;
  var group = age >= 18 ? 'adult' : 'child';
  print('Zayan is a $group.');
}
output
Zayan is a child.

Example 5 — Conditional Inside Interpolation

dart
void main() {
  var messages = 3;
  print('$messages new ${messages == 1 ? 'message' : 'messages'}');
}
output
3 new messages

Example 6 — Building Text with a Cascade

dart
void main() {
  var card = StringBuffer()
    ..writeln('Name: Sara')
    ..writeln('Age: 18')
    ..write('City: Lahore');
  print(card);
}
output
Name: Sara
Age: 18
City: Lahore

Example 7 — Student Result

dart
void main() {
  var math = 80;
  var science = 45;
  var english = 62;
  var passed = 0;

  passed += math >= 50 ? 1 : 0;
  passed += science >= 50 ? 1 : 0;
  passed += english >= 50 ? 1 : 0;

  var report = StringBuffer()
    ..writeln('Student: Ali')
    ..writeln('Science: ${science >= 50 ? 'Pass' : 'Fail'}')
    ..write('Subjects passed: $passed of 3');
  print(report);
}
output
Student: Ali
Science: Fail
Subjects passed: 2 of 3

How it works

  1. Each ? 1 : 0 turns a comparison into a number: 1 for a pass, 0 for a fail.
  2. += adds that number to passed, which ends at 1 + 0 + 1, or 2.
  3. Inside the report, another conditional expression turns science >= 50 into a word.
  4. One cascade builds the whole report, and one print shows it.

Example 8 — Shopping Total

dart
void main() {
  var price = 300;
  var quantity = 0;

  quantity++;
  quantity++;
  quantity++;

  var subtotal = price * quantity;
  var discount = subtotal >= 1000 ? 100 : 0;
  var total = subtotal - discount;

  print('Quantity: $quantity');
  print('Subtotal: $subtotal');
  print('Discount: $discount');
  print('Total: $total');
}
output
Quantity: 3
Subtotal: 900
Discount: 0
Total: 900

How it works

  1. Three ++ lines add one item each, so quantity becomes 3.
  2. 300 * 3 gives a subtotal of 900.
  3. 900 >= 1000 is false, so the conditional expression picks 0 — no discount.
  4. Try changing price to 400: the subtotal becomes 1200 and the discount 100.

Example 9 — Bank Balance

dart
void main() {
  var balance = 1000;
  var withdrawal = 1500;
  var transactions = 0;

  var allowed = withdrawal <= balance;
  balance -= allowed ? withdrawal : 0;
  transactions++;

  print(allowed ? 'Withdrawal approved' : 'Insufficient funds');
  print('Balance: $balance');
  print('Transactions: $transactions');
}
output
Insufficient funds
Balance: 1000
Transactions: 1

How it works

  1. allowed stores the answer to "is there enough money?" — here false.
  2. The conditional expression subtracts withdrawal only when allowed, otherwise 0, so the balance stays at 1000.
  3. transactions++ records the attempt either way.
  4. A second conditional expression picks the message to print.

Example 10 — Temperature Report

dart
void main() {
  var celsius = 29.5;
  celsius++;
  celsius++;

  var feel = celsius >= 30 ? 'hot' : 'pleasant';
  var report = StringBuffer()
    ..write('Afternoon: $celsius °C')
    ..write(' ($feel)');
  print(report);
}
output
Afternoon: 31.5 °C (hot)

How it works

  1. celsius is a double, and each ++ still adds exactly 1: 29.5 → 30.5 → 31.5.
  2. 31.5 >= 30 is true, so feel becomes 'hot'.
  3. The cascade writes two pieces into one buffer, which print shows as one line.

Common Beginner Mistakes

1. Using ++ on a final variable

dart
// final count = 1;
// count++;
output
Error: Can't assign to the final variable 'count'.

++ changes the variable, so it must be declared with var or a type, not final or const.

2. Expecting print(a++) to print the new value

dart
void main() {
  var a = 5;
  print(a++);
}
output
5

Postfix gives back the old value. Increment on its own line first, then print.

3. Writing x = x++

dart
void main() {
  var x = 5;
  x = x++;
  print(x);
}
output
5

No error — and no change. x++ adds one to x, but gives back the old 5, which the assignment then stores straight back into x. Write just x++;.

4. Forgetting the : part

dart
// var mark = 72;
// print(mark >= 50 ? 'Pass');
output
Error: Expected ':' before this.
  print(mark >= 50 ? 'Pass');
                           ^

A conditional expression always needs both values — there must be an answer for "no".

5. Storing the result of a method that returns nothing

dart
// var receipt = StringBuffer().write('Notebook');
// print(receipt);
output
Error: This expression has type 'void' and can't be used.
  print(receipt);
        ^

write gives back nothing (void), so receipt holds nothing usable. Use a cascade, StringBuffer()..write('Notebook'), which gives back the buffer itself.

6. Using a cascade to get a method's result

word..toUpperCase() gives back word, not the upper-case text (section 14). Use a normal . when you need what the method returns.

7. Nesting conditional expressions too deeply

a ? x : b ? y : z is legal, but hard to read. When a choice has more than two outcomes, wait for if, else if, and switch in Lectures 022–025.


Best Practices

  • Put ++ and -- on a line of their own; avoid using their result inside other expressions.
  • Prefer count++ for "add one" and += for any other amount.
  • Use a conditional expression to choose between two values, not to run actions.
  • Keep both sides of ? : the same type.
  • Use ?? instead of x != null ? x : fallback.
  • Use cascades for a series of changes to one object — write one operation per line.
  • Start a cascade with ?.. only when the object's type is nullable.

Mini Challenge

Store a player's name 'Sara', a starting score of 9, and a target of 10. The player scores one more point (use ++). Build a message with a StringBuffer and a cascade, and use a conditional expression to show 'Level complete!' if the score reached the target, or 'Keep going!' otherwise.

Your program should print exactly:

output
Player: Sara
Score: 10
Level complete!

Try it yourself before opening the solution.

Show solution
dart
void main() {
  var player = 'Sara';
  var score = 9;
  var target = 10;

  score++;

  var message = StringBuffer()
    ..writeln('Player: $player')
    ..writeln('Score: $score')
    ..write(score >= target ? 'Level complete!' : 'Keep going!');
  print(message);
}
output
Player: Sara
Score: 10
Level complete!

How it works

  1. score++ changes the score from 9 to 10.
  2. The cascade creates the buffer and adds three pieces of text to it.
  3. 10 >= 10 is true, so the conditional expression writes 'Level complete!'.

Practice Questions

Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.

Beginner

  1. Create var steps = 99;, increase it by one with ++, and print it.
  2. Create var stock = 4;, decrease it by one with -- twice, and print it.
  3. Create var age = 20; and use a conditional expression to print 'Adult' if age is at least 18, otherwise 'Minor'.

Application

  1. Without running it first, predict what this prints, then check: var n = 3; print(n++); print(n); print(++n);
  2. Store var count = 1; and print 1 apple or N apples with one print and a conditional expression inside the interpolation. Then change count to 5 and run it again.
  3. Store var balance = -200; and print Status: overdrawn when the balance is below zero, otherwise Status: OK.
  4. Use one StringBuffer and a cascade to build these three lines, then print the buffer: Name: Zayan, Age: 6, Grade: 1.
  5. Store var celsius = 18.5;, raise it by one with ++ three times, and print It is 21.5 °C, warm — the word must be warm if the temperature is at least 20, otherwise cool.

Challenge

  1. Store three marks — 90, 40, 55 — and a counter var passed = 0;. Using only += and conditional expressions (no if), count how many marks are at least 50, then print Passed: 2.
  2. Create var code = '';, then String? coupon = code.isEmpty ? null : code; and var total = 1000;. Print the amount to pay, which is 900 when there is a coupon and 1000 when there is not, using a conditional expression. Then create StringBuffer? log; built as coupon != null ? StringBuffer() : null, write 'Coupon used' into it with a null-aware cascade, and print log.

Practice Answers

Question 1 — Solution

dart
void main() {
  var steps = 99;
  steps++;
  print(steps);
}
output
100

Question 2 — Solution

dart
void main() {
  var stock = 4;
  stock--;
  stock--;
  print(stock);
}
output
2

Question 3 — Solution

dart
void main() {
  var age = 20;
  print(age >= 18 ? 'Adult' : 'Minor');
}
output
Adult

Question 4 — Solution

dart
void main() {
  var n = 3;
  print(n++);
  print(n);
  print(++n);
}
output
3
4
5

n++ prints the old value 3 and leaves n at 4. ++n adds one first, so it prints 5.

Question 5 — Solution

dart
void main() {
  var count = 1;
  print('$count ${count == 1 ? 'apple' : 'apples'}');
}
output
1 apple

With count = 5, the same program prints 5 apples.

Question 6 — Solution

dart
void main() {
  var balance = -200;
  print('Status: ${balance < 0 ? 'overdrawn' : 'OK'}');
}
output
Status: overdrawn

Question 7 — Solution

dart
void main() {
  var info = StringBuffer()
    ..writeln('Name: Zayan')
    ..writeln('Age: 6')
    ..write('Grade: 1');
  print(info);
}
output
Name: Zayan
Age: 6
Grade: 1

Question 8 — Solution

dart
void main() {
  var celsius = 18.5;
  celsius++;
  celsius++;
  celsius++;
  print('It is $celsius °C, ${celsius >= 20 ? 'warm' : 'cool'}');
}
output
It is 21.5 °C, warm

Question 9 — Solution

dart
void main() {
  var math = 90;
  var science = 40;
  var english = 55;
  var passed = 0;

  passed += math >= 50 ? 1 : 0;
  passed += science >= 50 ? 1 : 0;
  passed += english >= 50 ? 1 : 0;

  print('Passed: $passed');
}
output
Passed: 2

Each conditional expression turns a comparison into 1 or 0, and += adds them up: 1 + 0 + 1.

Question 10 — Solution

dart
void main() {
  var code = '';
  String? coupon = code.isEmpty ? null : code;
  var total = 1000;

  var toPay = coupon != null ? total - 100 : total;
  print(toPay);

  StringBuffer? log = coupon != null ? StringBuffer() : null;
  log?..write('Coupon used');
  print(log);
}
output
1000
null

There is no coupon, so the conditional expression picks total, and log is null. The ?.. cascade sees null and skips the write without crashing, so print(log) shows null.


Quick Revision

  1. What is a unary operator? Give two examples.
  2. What does count++ do to count?
  3. What value does a++ give back? What does ++a give back?
  4. Why does x = x++ leave x unchanged?
  5. What are the three parts of a conditional expression?
  6. When should you use ?? instead of ? :?
  7. What does a cascade x..method() give back?
  8. When do you need ?.. instead of ..?

Key Takeaways

  • A unary operator works on one value: -x, !flag, x++, x--.
  • ++ adds one and -- subtracts one, storing the result back — on int and double variables that can be changed.
  • Postfix a++ gives back the old value; prefix ++a gives back the new one. On a line of its own, there is no difference.
  • condition ? a : b chooses between two values; only the chosen side is worked out.
  • A conditional expression is a value, so it fits in assignments and in ${...}.
  • A StringBuffer collects text with write and writeln.
  • The cascade .. performs several operations on one object and gives back the object, not the method's result.
  • ?.. skips the whole cascade when the object is null.

What's Next?

Lecture 21 — Operator Precedence & Custom Operators. We now know almost every operator Dart has. But when several of them meet in one expression — price + tax * 2 > 100 && isMember ? 'free' : 'paid' — which runs first? Next we learn Dart's precedence rules, how brackets override them, and get a first look at how types define their own operators.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.