Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Records and Tables

Records and tables are AetherShell’s structured data types. Records are key-value maps; tables are arrays of records with a defined schema. Together they enable typed data processing pipelines.

Records

Creating Records

let person = {name: "Ada", age: 36, active: true}
let config = {host: "localhost", port: 8080, debug: false}

Record keys are always strings. Values can be any type, including nested records and arrays:

let project = {
    name: "AetherShell",
    version: {major: 0, minor: 3, patch: 0},
    tags: ["shell", "rust", "ai"]
}

Field Access

Use dot notation to access fields:

person.name      # => "Ada"
person.age       # => 36
project.version.major    # => 0

Accessing a non-existent field produces an error:

person.email     # Error: field 'email' not found in record

Record Operations

# Get all keys
{name: "Ada", age: 36} | keys
# => ["age", "name"]   (sorted alphabetically)

# Merge records (later values win)
let defaults = {color: "blue", size: 10}
let custom = {size: 20, bold: true}
merge defaults custom
# => {bold: true, color: "blue", size: 20}

Records in Pipelines

Records flow through pipelines as structured data:

let users = [
    {name: "Ada", score: 95},
    {name: "Bob", score: 82},
    {name: "Eve", score: 91}
]

users
| where fn(u) => u.score > 85
| map fn(u) => {name: u.name, grade: "A"}
# => [{name: "Ada", grade: "A"}, {name: "Eve", grade: "A"}]

Tables

Tables are structured data with named columns, returned by many builtins.

Table Structure

A table has:

  • rows: Array of records (each row is a {key: value} map)
  • schema: List of column names defining the structure

Built-in Table Sources

# List files — returns a table
ls "."
# Columns: name, path, ext, is_dir, size, modified

# Process listings
ps
# Columns: pid, name, cpu, memory

Pretty Printing

Tables get special column-aligned display in the terminal:

┌──────────────┬──────┬─────┬────────┐
│ name         │ ext  │ dir │ size   │
├──────────────┼──────┼─────┼────────┤
│ main.rs      │ rs   │ no  │ 2,451  │
│ lib.rs       │ rs   │ no  │ 1,089  │
│ Cargo.toml   │ toml │ no  │ 456    │
└──────────────┴──────┴─────┴────────┘

Data Pipeline Operations

select — Project Fields

Keep only specific columns:

ls "." | select "name" "size"
# Records with only name and size fields

where — Filter Rows

Keep rows matching a predicate:

ls "." | where fn(f) => f.size > 1000
ls "." | where fn(f) => f.ext == "rs"

map — Transform Rows

Create new values from each row:

ls "." | map fn(f) => {
    file: f.name,
    kb: f.size / 1024
}

sort — Order Rows

[3, 1, 4, 1, 5] | sort
# => [1, 1, 3, 4, 5]

group / group_by — Group Rows

Group records by a field value:

ls "." | group "ext"
# Records grouped by file extension

reduce — Aggregate

Collapse an array into a single value:

ls "." | map fn(f) => f.size | reduce fn(a, b) => a + b, 0
# Total size of all files

first / last — Take Elements

[1, 2, 3, 4, 5] | first 3    # => [1, 2, 3]
[1, 2, 3, 4, 5] | last 2     # => [4, 5]

reverse — Reverse Order

[1, 2, 3] | reverse    # => [3, 2, 1]

unique — Remove Duplicates

[1, 2, 2, 3, 3, 3] | unique    # => [1, 2, 3]

columns — Get Column Names

ls "." | columns
# => ["ext", "is_dir", "modified", "name", "path", "size"]

Format Conversion

Convert between structured data and serialization formats:

# JSON
let data = from_json '{"name": "test"}'
data | to_json

# CSV
let csv_data = from_csv "name,age\nAda,36\nBob,30"
csv_data | to_csv

# YAML
let yaml_data = from_yaml "name: test\ncount: 42"
yaml_data | to_yaml

Practical Examples

Analyze project files

ls "src"
| where fn(f) => f.ext == "rs"
| map fn(f) => {name: f.name, kb: f.size / 1024}
| sort
| reverse
# Rust files sorted by size, largest first

Process API response

http_get "https://api.github.com/repos/user/repo/issues"
| from_json
| where fn(i) => i.state == "open"
| map fn(i) => {title: i.title, labels: i.labels | map fn(l) => l.name}
| first 10

Build a report

let files = ls "src" | where fn(f) => f.ext == "rs"
let total_size = files | map fn(f) => f.size | reduce fn(a, b) => a + b, 0
let count = files | length

{
    total_files: count,
    total_bytes: total_size,
    avg_size: total_size / count,
    largest: files | sort | reverse | first 1
}