Tea — a strongly typed scripting language
Tea
DocsPlaygroundExamplesReferenceCommunity
GitHub

Tea Language

A strongly typed scripting language for native tools.

© 2026 Tea Language.

Agent documentationContributingCommunityRepository

Get Started

InstallationGetting StartedCLI

Tutorial

Build a CLI with Tea1. Display your first task2. Display several tasks3. Track task status4. Accept commands5. Read saved tasks6. Add tasks7. Complete tasks8. Organize and test9. Build your executable

Language

SyntaxTypesFunctionsStructsGenericsPattern MatchingError HandlingModulesTesting

Project

Code StyleContributing

Read as Markdown

Tutorial

Track task status

Model tasks with structs and format them with a function.

Chapter 3 of 9 · Build a CLI with Tea

Choose a chapter
  1. Build a CLI with Tea
  2. 1. Display your first task
  3. 2. Display several tasks
  4. 3. Track task status
  5. 4. Accept commands
  6. 5. Read saved tasks
  7. 6. Add tasks
  8. 7. Complete tasks
  9. 8. Organize and test
  10. 9. Build your executable

Start with chapter 2's numbered list. A title alone can't tell us whether a task is complete. We'll give each task a title and a boolean status, then move its display logic into a function.

Describe a task

Add this definition at the top of tasks.tea:

struct Task {
  title: String
  done: Bool
}

A struct groups named fields into a type. Fields have explicit types: title is a String, and done is a Bool (true or false). Struct definitions use braces; function and loop bodies use end.

Replace your string list with the Task(...) values in the complete checkpoint below. Named arguments make it clear which field each value supplies. The list's inferred type is now List[Task].

Give formatting a name

Add this function after the struct:

def format_task(task: Task, number: Int) -> String
  const marker = if(task.done) "[x]" else "[ ]"
  `${number}. ${marker} ${task.title}`
end

def introduces a function. Parameters have explicit types, and -> String declares the return type. Tea uses the final expression as the return value; you can also write return explicitly.

The if(...) ... else ... expression chooses one of two strings. Access a field with a dot, such as task.done or task.title. Both branches produce a String, so Tea can infer the type of marker.

Replace the loop's output statement with @println(format_task(task, number)), and rename its loop binding from title to task.

Run the program

tea tasks.tea
1. [ ] Learn Tea
2. [ ] Build a CLI
3. [x] Take a break

Complete checkpoint

tasks.tea

struct Task {
  title: String
  done: Bool
}

def format_task(task: Task, number: Int) -> String
  const marker = if(task.done) "[x]" else "[ ]"
  `${number}. ${marker} ${task.title}`
end

const tasks = [
  Task(title: "Learn Tea", done: false),
  Task(title: "Build a CLI", done: false),
  Task(title: "Take a break", done: true)
]
var number = 1

for task in tasks
  @println(format_task(task, number))
  number = number + 1
end

Try it yourself

Count completed tasks and print the count after the loop. Use a mutable counter and an if statement inside the loop.

Hint

Initialize var completed = 0 before the loop. Inside it, add:

if task.done
  completed = completed + 1
end

After the loop, print completed. Unlike the expression that chooses marker, this if performs an action and ends with end.

The task data and its presentation now have names. Next, we'll let the person running the program decide which tasks to see.

Read more: structs, functions and return values.

Previous2. Display several tasksNext4. Accept commands