Skip to content

Your First Program

Let’s write your first Shape program and build intuition for expression-oriented, typed data code.

print("Hello, Shape")

Arrays are indexed with [i], starting at 0. The runtime rejects out-of-bounds reads with Index N out of bounds (length M).

let values = [20, 21, 19, 24, 22]
print(values[0]) // first
print(values[values.len() - 1]) // last
let samples = [10.0, 12.0, 11.0, 14.0, 13.0, 12.0]
let total = samples.sum()
let count = samples.len()
let avg = total / count
print(f"count={count}, total={total}, avg={avg}")
let readings = [18, 22, 27, 19, 30, 25]
let threshold = 24
let high = readings.filter(|v| v > threshold)
print(high) // [27, 30, 25]
fn is_anomaly(value: number, mean: number, std: number) -> bool {
value > (mean + 2 * std)
}
let series = [20.1, 20.4, 21.0, 20.3, 25.8]
let total = series.sum()
let mean = total / series.len()
let latest = series[series.len() - 1]
// Approximate std for the example; for real code, use std::core::math::std.
let approx_std = 1.0
if is_anomaly(latest, mean, approx_std) {
print("anomaly detected")
} else {
print("normal")
}
fn bucket(n: int) -> string {
match n {
x: int where x < 0 => "negative"
x: int where x == 0 => "zero"
_ => "positive"
}
}
print(bucket(-3))
print(bucket(0))
print(bucket(8))

Shape requires typed records (declared with type) for field assignment and structural access. Anonymous object literals ({ x: 1, y: 2 }) are read-only — they cannot grow new fields at runtime.

type Record {
id: int,
label: string,
score: int,
}
let mut record = Record { id: 42, label: "alpha", score: 0 }
record.score = 99
print(f"{record.label}: {record.score}")

Create analysis.shape:

fn summarize(values: Vec<number>) -> string {
let count = values.len()
let total = values.sum()
let avg = total / count
f"count={count}, total={total}, avg={avg}"
}
fn main() {
let values = [3.2, 4.1, 3.8, 5.0, 4.4]
print(summarize(values))
}
main()

Run it:

Terminal window
shape run analysis.shape

Expected output:

count=5, total=20.5, avg=4.1
  • Shape is expression-oriented.
  • fn is the function declaration syntax.
  • Types are inferred where possible, and required at function boundaries.
  • Vectors, typed records, and pattern matching compose naturally.

Continue to Basic Concepts.