Cookbook

Paste-ready recipes for Weft 0.3.x. Save as .weft, run with weft run file.weft.

1 Hello and style 2 Files and text 3 JSON and config 4 HTTP client 5 HTTP server 6 Errors without try/catch 7 Enums and match 8 Closures and handlers 9 Lists, map, filter 10 Concurrency 11 CLI tools 12 Environment and secrets 13 LLM and agents 14 Packages and multi-file 15 Testing 16 Time, strings, regex 17 Data: CSV and SQLite 18 Logging and production 19 Packet captures 20 Troubleshooting

1 Hello and style

fn main {
    say("hello, weft")
}

Bindings, loops, pipeline

fn double(x) { x * 2 }

fn main {
    mut n := 0
    for x in [1, 2, 3] { n = n + x }
    name := "weft"
    say("sum=$n hi $name double=${double(21)}")
    21 |> double |> say
}

2 Files and text

fn main -> Result {
    text := fs.read("notes.txt")?
    fs.write("notes.bak", text)?
    fs.append("log.txt", "ran once\n")?
    say(len(text))
}

Paths and listing

fn main -> Result {
    root := fs.cwd()
    path := fs.join(root, "data", "users.jsonl")
    say(fs.base(path), fs.ext(path))
    for p in fs.glob("**/*.weft") { say(p) }
}

Grep lines

fn main -> Result {
    lines := fs.lines("access.log")?
    errors := filter(lines, fn(l) { str.contains(l, "ERROR") })
    say("${len(errors)} errors")
}

3 JSON and config

fn main -> Result {
    data := json.parse(`{"city":"Paris","temp":21}`)?
    say(data.city, data.temp)
    say(json.pretty(data))
}

YAML / TOML / INI

fn main -> Result {
    y := yaml.parse(fs.read("app.yaml")?)?
    t := toml.parse(fs.read("app.toml")?)?
    i := ini.parse(fs.read("app.ini")?)?
    say(y, t, i)
}

Nested get with default

fn main -> Result {
    data := json.parse(`{"user":{"name":"Ada"}}`)?
    say(json.get(data, "user.name")?)
    say(json.get(data, "user.missing", "anon")?)
}

4 HTTP client

fn main -> Result {
    data := http.get_json("https://httpbin.org/json")?
    say(json.pretty(data))
}

POST JSON

fn main -> Result {
    payload := json.stringify({"hello": "weft"})
    body := http.post("https://httpbin.org/post", payload)?
    say(str.slice(body, 0, 200))
}

Fan-out several URLs

fn fetch(url) -> Result { len(http.get(url)?) }

fn main -> Result {
    urls := ["https://example.com", "https://httpbin.org/get"]
    sizes := map(urls, fetch)?
    say(sizes)
}

5 HTTP server

fn main {
    http.serve(":8080", fn(req) {
        match req.path {
            "/health"  { http.json({"ok": true}) }
            "/version" { http.json({"v": "0.3.32"}) }
            _          { http.text(404, "not found") }
        }
    })
}

6 Errors without try/catch

fn load(path) -> Result {
    ensure(path != "", "path required")?
    fs.read(path).context("load")?
}

fn main -> Result {
    text := load("README.md")?
    say(len(text))
}

Defaults and inspection

fn main {
    n := int.parse("nope").unwrap_or(0)
    say("n=$n")

    r := fs.read("missing.txt")
    if r.is_err {
        say("kind=${r.err.kind} msg=${r.err.message}")
    }
}

7 Enums and match

enum Status { Pending, Running, Done, Failed }

fn label(s) {
    match s {
        Status.Pending { "..." }
        Status.Running { "=>" }
        Status.Done    { "ok" }
        Status.Failed  { "!!" }
        _              { "?" }
    }
}

fn main { say(label(Status.Running)) }

Sum types with payloads

enum Shape { Circle(r), Rect(w, h), Point }

fn area(s) {
    match s {
        Shape.Circle(r)  { 3.14 * r * r }
        Shape.Rect(w, h) { w * h }
        Shape.Point      { 0 }
    }
}

fn main { say(area(Shape.Circle(5))) }

8 Closures and handlers

fn main {
    prefix := "item"
    out := map([1, 2, 3], fn(n) { "$prefix-$n" })
    say(out)
}

By-value capture

fn main {
    mut n := 1
    g := fn() { n }
    n = 99
    say(g())   // 1 — frozen when g was created
}

9 Lists, map, filter

fn main {
    // concurrent by default
    say(map([1, 2, 3, 4], fn(x) { x * x }))

    // filter
    evens := filter([1, 2, 3, 4, 5, 6], fn(n) { n % 2 == 0 })
    say(evens)

    // sequential when side-effect order matters
    seq_map(["a", "b"], fn(x) { say(x) })
}

10 Concurrency

fn main -> Result {
    results := parallel([
        fn() { 1 + 1 },
        fn() { 2 * 3 },
        fn() { 10 - 1 },
    ])?
    say(results)
}

Channels

fn producer(ch) {
    send(ch, 1)
    send(ch, 2)
    close(ch)
}

fn main -> Result {
    ch := channel(2)
    spawn(producer, ch)
    say(recv(ch)?, recv(ch)?)
}

Race and timeout

fn slow() { time.sleep(2); "slow" }
fn fast() { "fast" }

fn main -> Result {
    say(race([fn() { slow() }, fn() { fast() }])?)
}

11 CLI tools

// weft run tool.weft -- greet Ada

fn main -> Result {
    p := cli.parse({
        "about": "demo tool",
        "flags": {
            "verbose": {"short": "v", "bool": true},
            "env": {"short": "e", "default": "dev"},
        },
    })?
    if p.help { say(p.usage); cli.exit(0) }

    cmd := p.args[0]
    if cmd == "greet" {
        name := if len(p.args) > 1 { p.args[1] } else { "world" }
        say("hello, $name (${p.env})")
    }
}

Scaffold a new CLI: weft new cli mytool

12 Environment and secrets

fn main -> Result {
    home := env.get("HOME")
    if home == null { say("HOME unset") }
    else { say("home=$home") }

    token := env.require("API_TOKEN")?
    say("token length=${len(token)}")
}

13 LLM and agents

One-shot chat

fn main -> Result {
    reply := llm.chat("Explain Result in one line")?
    say(reply)

    // system prompt
    say(llm.chat("hi", {"system": "Be terse."})?)

    // multi-turn
    say(llm.chat([
        {"role": "system", "content": "You write short answers."},
        {"role": "user", "content": "What is Weft?"},
    ])?)
}

Tool calling

fn add(a, b) { a + b }
fn note(msg) { "noted: $msg" }

fn main -> Result {
    reply := llm.ask("What is 2+3?", [
        llm.tool("add", add, "add two numbers"),
        llm.tool("note", note, "record a note"),
    ])?
    say(reply)
}

Multi-step agent

fn search(query) { http.get_json("https://api.example.com/search?q=$query")? }
fn lookup(id) { db.query("SELECT * FROM items WHERE id = ?", [id])? }

fn main -> Result {
    reply := llm.ask("Find the cheapest widget and get its details", [
        llm.tool("search", search),
        llm.tool("lookup", lookup),
    ], {"max_steps": 6})?
    say(reply)
}

Streaming

fn main -> Result {
    // token-by-token events
    for e in llm.stream("one word: hi")? {
        if e.kind == "text" { print(e.text) }
    }
    say("")

    // or collect into one string
    say(llm.stream_text("one word: hi")?)
}

Structured extraction

fn main -> Result {
    data := llm.extract("Paris is the capital of France", {
        "city": "string",
        "country": "string",
    })?
    say(data.city)    // "Paris"
    say(data.country) // "France"
}

Local with Ollama

$ export WEFT_PROVIDER=ollama
$ export OLLAMA_MODEL=llama3.2
$ weft run agent.weft
fn main -> Result {
    say(ollama.chat("hello from weft")?)
}

Structured models (mold)

$ weft registry install mold
use mold

fn main -> Result {
    User := mold.model({
        "name":  mold.string({"min": 1}),
        "email": mold.string({"format": "email"}),
        "role":  mold.one_of(["admin", "user"]),
    })

    // generate tool params for the model
    spec := mold.tool_params(User)
    reply := llm.ask("Create user Alice, admin", [
        llm.tool("create", create_user, spec),
    ])?
    say(reply)
}

14 Packages and multi-file

Path import

// lib/math.weft
pub fn add(a, b) { a + b }

// main.weft
use "./lib/math.weft" as math

fn main { say(math.add(2, 3)) }

Install from registry

$ weft init myapp && cd myapp
$ weft registry install mold
$ weft registry install ml

Author a module

$ weft new module greeter
$ cd greeter && weft mod check

15 Testing

// math_test.weft
fn test_add {
    test.eq(1 + 1, 2)
    test.is_true(len([1, 2]) == 2)
}

fn test_parse {
    n := int.parse("3").unwrap_or(0)
    test.eq(n, 3)
}
$ weft test
$ weft test -run add -q

16 Time, strings, regex

fn main {
    now := time.now()
    say(time.iso(now))
    say(time.format(now, "2006-01-02"))

    say(str.trim("  hello  "))
    say(str.split("a,b,c", ","))

    m := re.find(`[0-9]+`, "ab42cd")
    say(m)
}

17 Data: CSV and SQLite

fn main -> Result {
    c := db.open("sqlite:app.db")?
    c.exec("CREATE TABLE IF NOT EXISTS items(id INTEGER PRIMARY KEY, name TEXT, qty INTEGER)")?
    c.exec("INSERT INTO items(name, qty) VALUES (?, ?)", ["widgets", 10])?

    rows := c.query("SELECT * FROM items ORDER BY name")?
    for row in rows { say(row.id, row.name, row.qty) }
    c.close()?
}

CSV

fn main -> Result {
    rows := csv.read("metrics.csv")?
    say("${len(rows)} rows")
}

18 Logging and production

fn main -> Result {
    log.set_level("info")
    log.info("starting worker")

    path := env.get("INPUT")
    if path == null {
        return bail("INPUT required", "config")
    }
    raw := fs.read(path).context("read input")?
    log.info("read bytes=${len(raw)}")
    Ok(unit)
}

19 Packet captures

use pcap

fn main -> Result {
    pkt := pcap.ethernet({
        "dst": "ff:ff:ff:ff:ff:ff",
        "src": "00:11:22:33:44:55",
        "payload": pcap.ipv4({
            "src": "192.168.1.100",
            "dst": "93.184.216.34",
            "payload": pcap.tcp({
                "src_port": 49152,
                "dst_port": 443,
                "flags": "SYN",
            }),
        }),
    })
    pcap.write("handshake.pcap", [pkt])?
    say("wrote handshake.pcap")
}

Read a capture

use pcap

fn main -> Result {
    pkts := pcap.read("capture.pcap")?
    say("${len(pkts)} packets")
    for p in pkts { say("ts:", p.ts, "len:", p.len) }
}

20 Troubleshooting

SymptomFix
no main functionAdd fn main { ... }
? outside ResultMark fn ... -> Result
Import not foundweft install; check vendor/
Closure sees old valueBy design — capture is by value
Unknown stdlib nameweft stdlib / weft stdlib pkg
Types look wrongweft check file.weft --types
$ weft doctor
$ weft check .
$ weft test -q