Get Vary running locally with Docker, verify the compiler, and run a first file from your working directory.
Check that Docker is installed before pulling the Vary image.
docker --version
Run: docker --version
Expected output:
^Docker version [0-9]+\.[0-9]+\.[0-9]+, build [0-9a-f]+$
Create a local vary command that runs the compiler container in your current directory.
alias vary='docker run --rm \
-u "$(id -u):$(id -g)" \
-v "$(pwd):/workspace" \
-w /workspace \
ghcr.io/ccollicutt/vary:latest'
vary --version
Run: vary --version
Expected output:
^Vary v[0-9]+-alpha\.[0-9]+ .+$
Print the working directory and verify it is the container's /workspace mount.
import system
let here: Str = system.cwd().to_str()
print("cwd=" + here)
Run: vary run main.vary
Expected output:
cwd=/workspace
Ten short programs that show what Vary is for: typed code, pure boundaries, contracts, and tests strong enough to survive mutation.
Compose a deploy label from typed parts.
def deploy_label(env: Str, region: Str) -> Str {
return env + "-" + region
}
print(deploy_label("prod", "us-east-1"))
Run: vary run main.vary
Expected output:
prod-us-east-1
Sum response bytes in a pure helper and print at the edge.
data Chunk {
label: Str
bytes: Int
}
pure def total_bytes(chunks: List[Chunk]) -> Int {
mut sum = 0
for chunk in chunks {
sum = sum + chunk.bytes
}
return sum
}
let response = [
Chunk("headers", 412),
Chunk("body", 2048),
Chunk("trailers", 96),
]
print("bytes=" + str(total_bytes(response)))
Run: vary run main.vary
Expected output:
bytes=2556
Compute an exponential backoff so the next lessons have an obvious answer.
pure def backoff_ms(attempt: Int) -> Int {
if attempt <= 0 {
return 100
}
if attempt == 1 {
return 200
}
if attempt == 2 {
return 400
}
return 800
}
print("backoff=" + str(backoff_ms(2)) + "ms")
Run: vary run main.vary
Expected output:
backoff=400ms
Choose a release path with an enum and a top-level match.
enum ReleaseKind {
PATCH
MINOR
MAJOR
}
let release = ReleaseKind.MINOR
match release {
case ReleaseKind.PATCH {
print("release=patch")
}
case ReleaseKind.MINOR {
print("release=minor")
}
case ReleaseKind.MAJOR {
print("release=major")
}
}
Run: vary run main.vary
Expected output:
release=minor
Count rate-limited responses while keeping the response list immutable.
let responses: List[Int] = [200, 200, 429, 200, 503, 429, 200]
mut throttled = 0
for status in responses {
if status == 429 {
throttled = throttled + 1
}
}
print("throttled=" + str(throttled))
Run: vary run main.vary
Expected output:
throttled=2
Describe a deploy target with typed fields.
data DeployTarget {
service: Str
runtime: Str
arch: Str
}
let target = DeployTarget("billing-api", "JVM 21", "linux/amd64")
print(target.service + " -> " + target.runtime + " on " + target.arch)
Run: vary run main.vary
Expected output:
billing-api -> JVM 21 on linux/amd64
Return the first non-empty message and declare both ends of the contract.
pure def first_message(messages: List[Str]) -> Str {
in {
len(messages) > 0
}
out (value) {
len(value) > 0
}
for message in messages {
if len(message) > 0 {
return message
}
}
return "unknown"
}
let errors = ["", "timeout after 30s", "connection refused"]
print("first=" + first_message(errors))
Run: vary run main.vary
Expected output:
first=timeout after 30s
Define a typed record and a pure predicate suitable for vary check.
data Response {
endpoint: Str
status: Int
}
pure def is_2xx(response: Response) -> Bool {
return response.status >= 200 and response.status < 300
}
let response = Response("/api/users", 200)
if is_2xx(response) {
print("checked: " + response.endpoint)
}
Run: vary run main.vary
Expected output:
checked: /api/users
Compute a health verdict in a pure helper that a future test can assert against.
pure def health(failing: Int) -> Str {
if failing == 0 {
return "green"
}
if failing <= 2 {
return "amber"
}
return "red"
}
let probe_results = [True, True, False, True]
mut failing = 0
for ok in probe_results {
if not ok {
failing = failing + 1
}
}
print(health(failing))
Run: vary run main.vary
Expected output:
amber
Sum the timeout budget across a typed list of pipeline stages.
data Stage {
name: Str
timeout_s: Int
}
let pipeline = [
Stage("compile", 60),
Stage("unit", 30),
Stage("integration", 120),
Stage("deploy", 90),
]
mut budget = 0
for stage in pipeline {
budget = budget + stage.timeout_s
}
print("pipeline budget=" + str(budget) + "s")
Run: vary run main.vary
Expected output:
pipeline budget=300s
Twelve runnable lessons covering typed values, functions, data, state, enums, dictionaries, nullable values, modules, and tests.
Bind typed values and print a release string.
let project: Str = "acme-billing"
let major: Int = 2
let minor: Int = 14
print(project + " v" + str(major) + "." + str(minor))
Run: vary run main.vary
Expected output:
acme-billing v2.14
Classify an HTTP status with a pure helper.
pure def class_of(status: Int) -> Str {
if status >= 500 {
return "5xx"
}
if status >= 400 {
return "4xx"
}
if status >= 200 {
return "2xx"
}
return "other"
}
print("404 -> " + class_of(404))
Run: vary run main.vary
Expected output:
404 -> 4xx
Count how many requests exceeded a latency budget.
let durations_ms: List[Int] = [42, 180, 95, 240, 33]
mut slow: Int = 0
for d in durations_ms {
if d > 100 {
slow = slow + 1
}
}
print("slow=" + str(slow))
Run: vary run main.vary
Expected output:
slow=2
Define an immutable record and read its generated toString.
data Response {
status: Int
bytes: Int
}
let resp = Response(200, 1024)
print(str(resp))
Run: vary run main.vary
Expected output:
Response(status=200, bytes=1024)
Track remaining retries behind a small stateful class.
class RetryBudget(max: Int) {
mut remaining: Int = max
def consume(self) -> None {
self.remaining = self.remaining - 1
}
def left(self) -> Int {
return self.remaining
}
}
let budget = RetryBudget(3)
budget.consume()
print("remaining=" + str(budget.left()))
Run: vary run main.vary
Expected output:
remaining=2
Advance a build state machine with exhaustive matching.
enum BuildState {
QUEUED
RUNNING
PASSED
FAILED
}
pure def advance(state: BuildState) -> BuildState {
match state {
case BuildState.QUEUED {
return BuildState.RUNNING
}
case BuildState.RUNNING {
return BuildState.PASSED
}
case BuildState.PASSED {
return BuildState.PASSED
}
case BuildState.FAILED {
return BuildState.FAILED
}
}
}
print(str(advance(BuildState.QUEUED)))
Run: vary run main.vary
Expected output:
RUNNING
Fall back to a default when a configured value is missing.
let configured: Int? = None
mut port: Int = 8080
if configured is not None {
port = configured
}
print("port=" + str(port))
Run: vary run main.vary
Expected output:
port=8080
Look up a service timeout from a config map.
let timeouts: Dict[Str, Int] = {"db": 30, "http": 10, "cache": 60}
print("http=" + str(timeouts["http"]) + "s")
Run: vary run main.vary
Expected output:
http=10s
Format an artifact coordinate with a pure helper.
pure def coord(group: Str, name: Str, version: Str) -> Str {
return group + ":" + name + ":" + version
}
print(coord("acme", "billing", "1.4.2"))
Run: vary run main.vary
Expected output:
acme:billing:1.4.2
Print the inputs and outputs the next lesson will assert on.
pure def class_of(status: Int) -> Str {
if status >= 500 {
return "5xx"
}
if status >= 400 {
return "4xx"
}
return "2xx"
}
print(class_of(503) + "," + class_of(200))
Run: vary run main.vary
Expected output:
5xx,2xx
Run two observe assertions against the helper.
pure def class_of(status: Int) -> Str {
if status >= 500 {
return "5xx"
}
if status >= 400 {
return "4xx"
}
return "2xx"
}
test "server errors classify as 5xx" {
observe class_of(503) == "5xx"
}
test "client errors classify as 4xx" {
observe class_of(404) == "4xx"
}
Run: vary test tests/grade_test.vary
Expected output:
Results: 2 passed, 0 failed
Run a file with one intentional boundary mistake and find the summary line.
pure def class_of(status: Int) -> Str {
if status >= 500 {
return "5xx"
}
if status >= 400 {
return "4xx"
}
return "2xx"
}
test "server errors classify as 5xx" {
observe class_of(503) == "5xx"
}
test "boundary 500 should be 4xx" {
observe class_of(500) == "4xx"
}
Run: vary test tests/grade_test.vary
Expected output:
Results: 1 passed, 1 failed
Seven runnable lessons covering test, observe, edge cases, error paths, output, and naming.
Verify one fact about one function.
pure def is_even(n: Int) -> Bool {
return n % 2 == 0
}
test "4 is even" {
observe is_even(4) == True
}
Run: vary test tests/even_test.vary
Expected output:
Results: 1 passed, 0 failed
Group several related facts under one named behaviour.
pure def normalize_path(path: Str) -> Str {
if path.startswith("/") {
return path
}
return "/" + path
}
test "normalize_path enforces a single leading slash" {
observe normalize_path("api") == "/api"
observe normalize_path("/api") == "/api"
observe normalize_path("") == "/"
}
Run: vary test tests/path_test.vary
Expected output:
Results: 1 passed, 0 failed
Cover distinct behaviours with separate named tests.
pure def class_of(status: Int) -> Str {
if status >= 500 {
return "5xx"
}
if status >= 400 {
return "4xx"
}
if status >= 200 {
return "2xx"
}
return "other"
}
test "2xx range" {
observe class_of(200) == "2xx"
}
test "4xx range" {
observe class_of(404) == "4xx"
}
test "5xx range" {
observe class_of(503) == "5xx"
}
test "below 200 is other" {
observe class_of(100) == "other"
}
Run: vary test tests/status_test.vary
Expected output:
Results: 4 passed, 0 failed
Assert both sides of every edge where behaviour changes.
pure def is_2xx(status: Int) -> Bool {
return status >= 200 and status < 300
}
test "200 is the lower edge" {
observe is_2xx(200) == True
observe is_2xx(199) == False
}
test "299 is the upper edge" {
observe is_2xx(299) == True
observe is_2xx(300) == False
}
Run: vary test tests/range_test.vary
Expected output:
Results: 2 passed, 0 failed
Use observe throws to assert a block raises.
def parse_port(s: Str) -> Int {
let n = int(s)
if n < 1 or n > 65535 {
raise "port out of range: " + s
}
return n
}
test "valid port parses" {
observe parse_port("8080") == 8080
}
test "out of range raises" {
observe throws { parse_port("99999") }
}
test "non-numeric raises" {
observe throws { parse_port("nope") }
}
Run: vary test tests/port_test.vary
Expected output:
Results: 3 passed, 0 failed
Locate the pass/fail summary line and understand the exit code.
pure def retry_delay_ms(attempt: Int) -> Int {
if attempt <= 0 {
return 100
}
if attempt >= 5 {
return 16000
}
mut delay: Int = 100
mut i: Int = 0
while i < attempt {
delay = delay * 2
i = i + 1
}
return delay
}
test "first attempt is 100ms" {
observe retry_delay_ms(0) == 100
}
test "doubles each attempt" {
observe retry_delay_ms(1) == 200
observe retry_delay_ms(2) == 400
observe retry_delay_ms(3) == 800
}
test "saturates at 16000ms" {
observe retry_delay_ms(5) == 16000
observe retry_delay_ms(10) == 16000
}
Run: vary test tests/backoff_test.vary
Expected output:
Results: 3 passed, 0 failed
Write test names that read like the spec.
pure def truncate(s: Str, limit: Int) -> Str {
if len(s) <= limit {
return s
}
return s[:limit - 1] + "…"
}
test "truncate returns the input when it fits inside the limit" {
observe truncate("hello", 10) == "hello"
}
test "truncate replaces the last character with an ellipsis when too long" {
observe truncate("hello world", 5) == "hell…"
}
test "truncate handles the exact-fit boundary without modification" {
observe truncate("hello", 5) == "hello"
}
Run: vary test tests/truncate_test.vary
Expected output:
Results: 3 passed, 0 failed
Five runnable lessons covering vary mutate, scores, survivors, boundary mutations, and the inner-loop workflow.
See that one happy-path test leaves most mutants alive.
pure def has_free_shipping(order_total: Int) -> Bool {
return order_total >= 50
}
test "larger order qualifies" {
observe has_free_shipping(75) == True
}
Run: vary mutate free_shipping.vary --tests free_shipping.vary --quick
Expected output:
(?m)Mutation score: \d{1,2}%
Add a boundary and an opposite-branch test until the score hits 100%.
pure def has_free_shipping(order_total: Int) -> Bool {
return order_total >= 50
}
test "larger order qualifies" {
observe has_free_shipping(75) == True
}
test "smaller order does not qualify" {
observe has_free_shipping(20) == False
}
test "exactly 50 qualifies" {
observe has_free_shipping(50) == True
}
test "49 does not qualify" {
observe has_free_shipping(49) == False
}
Run: vary mutate free_shipping.vary --tests free_shipping.vary --quick
Expected output:
(?m)Mutation score: 100%
See how range predicates leak mutants when tests skip the edges.
pure def is_2xx(status: Int) -> Bool {
return status >= 200 and status < 300
}
test "200 is 2xx" {
observe is_2xx(200) == True
}
test "404 is not 2xx" {
observe is_2xx(404) == False
}
Run: vary mutate is_2xx.vary --tests is_2xx.vary --quick
Expected output:
(?m)Mutation score: \d{1,2}%
Cover all four boundaries of a range predicate.
pure def is_2xx(status: Int) -> Bool {
return status >= 200 and status < 300
}
test "200 is the lower edge" {
observe is_2xx(200) == True
observe is_2xx(199) == False
}
test "299 is the upper edge" {
observe is_2xx(299) == True
observe is_2xx(300) == False
}
test "404 is not 2xx" {
observe is_2xx(404) == False
}
Run: vary mutate is_2xx.vary --tests is_2xx.vary --quick
Expected output:
(?m)Mutation score: 100%
Hit 100% on a multi-branch classifier with one edge pair per threshold.
pure def class_of(status: Int) -> Str {
if status >= 500 {
return "5xx"
}
if status >= 400 {
return "4xx"
}
if status >= 200 {
return "2xx"
}
return "other"
}
test "200 is the 2xx lower edge" {
observe class_of(200) == "2xx"
observe class_of(199) == "other"
}
test "400 is the 4xx lower edge" {
observe class_of(400) == "4xx"
observe class_of(399) == "2xx"
}
test "500 is the 5xx lower edge" {
observe class_of(500) == "5xx"
observe class_of(499) == "4xx"
}
test "deep 5xx still classifies as 5xx" {
observe class_of(503) == "5xx"
}
Run: vary mutate class_of.vary --tests class_of.vary --quick
Expected output:
(?m)Mutation score: 100%
Prepare an Ubuntu host, confirm Vary and Via are installed, install Via prerequisites, initialize the control plane, and verify the Via systemd services.
Confirm the host already has the Vary compiler and Via server CLI on PATH.
# Confirm the Vary compiler is installed and available on PATH.
command -v vary
vary --version
# Confirm the Via server CLI is installed and available on PATH.
command -v via
via --version
Run: via --version
Expected output:
(?m)^Via\s+.+$
Install Ubuntu packages for Git, Docker, Caddy, and the Via Docker network.
# Install Git, Docker, Caddy, and the Via Docker network.
via install --print-prereq-script | sudo bash
# Create Via users, directories, systemd units, and admin group access.
via install
Run: via install
Expected output:
(?m)^\s*Installed changes applied$|^\s*Installed no changes \(already installed\)$
Write server config, create the database, and mint signing keys.
# Write config, create the database, mint keys, and start Via services.
via init --domain vary.example.com
# Confirm the host layout, services, Docker, Git, and proxy are healthy.
via doctor
Run: via doctor
Expected output:
(?m)^.*PASS.*$
Confirm the control plane, builder, runner, config server, test runner, and managed Caddy service are active.
# Show every Via service and whether it is active.
via status
# Re-run the full host and runtime health checks after startup.
via doctor
Run: via status
Expected output:
(?m)\bactive\b
Create the first operator session, connect a local Vary project, test it, push a deploy, and inspect deploy output.
Mint a one-time admin token and create the app record that will receive source pushes.
# Create a one-time admin token for the first operator.
via admin create alice
# Create the server-side app record and bare Git repo.
via app ensure my-api --owner alice
Run: via app ensure my-api --owner alice
Expected output:
(?i)created|exists|ensured|my-api
Exchange the one-time admin token for workstation credentials.
# Exchange the one-time admin token for workstation credentials.
vary login https://vary.example.com --name alice --token-stdin
Run: vary login https://vary.example.com --name alice --token-stdin
Expected output:
(?i)session|credentials|logged in|expires
Record the Via server and app id in the project's vary.toml.
# Work from the local Vary project you want to deploy.
cd my-api
# Record the Via server URL and app name in vary.toml.
vary app init --server https://vary.example.com --app my-api
# Confirm only the deployment config changed.
git status --short
# Commit the config so deploys can push a clean tree.
git add vary.toml
git commit -m "Configure Via deployment"
Run: vary app init --server https://vary.example.com --app my-api
Expected output:
(?m)^.*vary\.toml.*$
Run the project's tests, push the current Git commit to Via, and wait for the deploy result.
# Run local tests before pushing source to Via.
vary test
# Via deploys expect committed source by default.
git status --short
# Push the current commit and wait for the deploy result.
vary app deploy
# Confirm the app's current deploy and runtime state.
vary app status
Run: vary app deploy
Expected output:
(?i)\brunning\b
Find the build id, inspect the build detail, and stream test logs for the deploy.
# List recent server builds for the app.
vary app builds --app my-api
# Inspect one build record in detail.
vary app build <build-id> --app my-api
# Stream the test logs when a result needs detail.
vary app logs --tests <build-id> --app my-api
Run: vary app builds --app my-api
Expected output:
(?i)build|running|passed|failed
Confirm the server saw the same test result by reading Via's local state directory.
# Read the server-side test result directly from the Via state directory.
vary app tests <build-id> --app my-api --local --state-dir /var/lib/via
Run: vary app tests <build-id> --app my-api --local --state-dir /var/lib/via
Expected output:
(?i)passed|failed|tests
Request a hostname, verify ownership, add a route, and stream runtime logs.
# Ask Via to create a domain ownership challenge.
vary app domain request my-api my-api.apps.example.com --target prod
# Confirm the required DNS record is visible.
vary app domain verify my-api my-api.apps.example.com --target prod
# Attach the verified hostname to the running app target.
vary app route add my-api my-api.apps.example.com --target prod --port 8080
# Stream runtime logs from the active container.
vary app logs --runtime
Run: vary app logs --runtime
Expected output:
(?m)^.*my-api.*$