Pyro by Example

Learn Pyro step by step with 50 runnable programs. Every example uses correct Pyro syntax and can be copied straight into the playground.

Basics

1. Hello World

The simplest Pyro program. No main function, no imports, no semicolons. Just write code and it runs.

print("Hello, World!")
Hello, World!
Try in Playground →

2. Variables

Bare assignment creates an immutable variable. Use mut for mutable, let for explicit immutable. No type annotations needed.

# Bare assignment — immutable by default
name = "Pyro"
version = 1.0
active = true

# mut — mutable variable
mut counter = 0
counter = counter + 1
print("counter = {counter}")

# let — explicit immutable
let pi = 3.14159
print("name={name}, version={version}, pi={pi}")
counter = 1 name=Pyro, version=1.0, pi=3.14159
Try in Playground →

3. String Interpolation

Embed variables and expressions inside strings with {var}. No f-prefix like Python — it just works.

name = "Aravind"
lang = "Pyro"
age = 20

# Variable interpolation
print("Hello, {name}!")
print("{name} created {lang}")

# Expression interpolation
print("2 + 2 = {2 + 2}")
print("Next year: {age + 1}")
print("{lang} is {79}x faster!")
Hello, Aravind! Aravind created Pyro 2 + 2 = 4 Next year: 21 Pyro is 79x faster!
Try in Playground →

4. Functions

Define functions with fn (not def). No colons, no type hints. Indentation-based blocks.

# Simple function
fn add(a, b)
    return a + b

print("3 + 5 = {add(3, 5)}")

# Function with default value
fn greet(name, greeting = "Hello")
    return "{greeting}, {name}!"

print(greet("World"))
print(greet("Pyro", "Welcome"))

# Functions can call other functions
fn square(x)
    return x * x

fn sum_of_squares(a, b)
    return square(a) + square(b)

print("3^2 + 4^2 = {sum_of_squares(3, 4)}")
3 + 5 = 8 Hello, World! Welcome, Pyro! 3^2 + 4^2 = 25
Try in Playground →

5. If / Else

Conditional blocks use if, elif, else with no colons. Indentation defines the block.

age = 18

if age >= 18
    print("You can vote!")
else
    print("Too young to vote.")

# elif chains
score = 85

if score >= 90
    grade = "A"
elif score >= 80
    grade = "B"
elif score >= 70
    grade = "C"
else
    grade = "F"

print("Score {score} = Grade {grade}")

# Nested conditions
x = 15
if x > 10
    if x % 2 == 0
        print("{x} is big and even")
    else
        print("{x} is big and odd")
You can vote! Score 85 = Grade B 15 is big and odd
Try in Playground →

6. For Loops

Use 0..10 ranges (not range(10)). Iterate over lists directly. No colons needed.

# Range loop: 0..5 gives [0, 1, 2, 3, 4]
for i in 0..5
    print("i = {i}")

# Iterate over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits
    print("I like {fruit}")

# Accumulator pattern
mut total = 0
for n in 1..11
    total = total + n
print("Sum 1..10 = {total}")
i = 0 i = 1 i = 2 i = 3 i = 4 I like apple I like banana I like cherry Sum 1..10 = 55
Try in Playground →

7. While Loops

Loop while a condition is true. Use mut for the counter since it changes each iteration.

# Countdown
mut counter = 5
while counter > 0
    print("T-minus {counter}")
    counter = counter - 1
print("Liftoff!")

# Find first power of 2 above 1000
mut val = 1
while val <= 1000
    val = val * 2
print("First power of 2 > 1000: {val}")
T-minus 5 T-minus 4 T-minus 3 T-minus 2 T-minus 1 Liftoff! First power of 2 > 1000: 1024
Try in Playground →

8. Lists

Lists are dynamic arrays with built-in methods: push, len, sum, min, max. Zero-indexed.

# Create and access
nums = [10, 20, 30, 40, 50]
print("First: {nums[0]}")
print("Last:  {nums[4]}")

# Modify with mut
mut colors = ["red", "green"]
colors.push("blue")
print("Colors: {colors}")

# Built-in aggregations
scores = [95, 87, 92, 78, 100]
print("Length: {scores.len()}")
print("Sum:    {scores.sum()}")
print("Min:    {scores.min()}")
print("Max:    {scores.max()}")
First: 10 Last: 50 Colors: [red, green, blue] Length: 5 Sum: 452 Min: 78 Max: 100
Try in Playground →

9. Maps / Dicts

Key-value dictionaries with {"key": "value"} syntax. Access with dot or bracket notation.

# Create a map
person = {"name": "Aravind", "age": 20, "lang": "Pyro"}

# Access values
print("Name: {person[\"name\"]}")
print("Age:  {person[\"age\"]}")

# Get keys and values
print("Keys:   {person.keys()}")
print("Values: {person.values()}")

# Iterate over map
scores = {"math": 95, "science": 88, "english": 92}
for key in scores.keys()
    print("{key}: {scores[key]}")
Name: Aravind Age: 20 Keys: [name, age, lang] Values: [Aravind, 20, Pyro] math: 95 science: 88 english: 92
Try in Playground →

10. Pipes & Lambdas

The pipe operator |> chains data left-to-right. Lambdas use |x| expr syntax — shorter than Python's lambda x:.

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Pipe to filter
evens = nums |> filter(|x| x % 2 == 0)
print("Evens: {evens}")

# Pipe to map
doubled = nums |> map(|x| x * 2)
print("Doubled: {doubled}")

# Chain pipes — reads like English
result = nums |> filter(|x| x > 5)
             |> map(|x| x * x)
print("Filter >5, square: {result}")

# Lambda as variable
triple = |x| x * 3
print("7 tripled = {triple(7)}")
Evens: [2, 4, 6, 8, 10] Doubled: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Filter >5, square: [36, 49, 64, 81, 100] 7 tripled = 21
Try in Playground →
Intermediate

11. String Methods

Built-in string methods for transformation, splitting, trimming, replacing, and repeating.

msg = "  Hello, Pyro!  "

print("upper:   {msg.upper()}")
print("lower:   {msg.lower()}")
print("trim:    [{msg.trim()}]")
print("replace: {msg.trim().replace(\"Pyro\", \"World\")}")

# Split and join
csv = "a,b,c,d,e"
parts = csv.split(",")
print("Split: {parts}")

# Repeat
star = "*".repeat(20)
print(star)

# Contains, starts_with, ends_with
path = "src/main.pyro"
print("Contains main: {path.contains(\"main\")}")
print("Ends with .pyro: {path.ends_with(\".pyro\")}")
upper: HELLO, PYRO! lower: hello, pyro! trim: [Hello, Pyro!] replace: Hello, World! Split: [a, b, c, d, e] ******************** Contains main: true Ends with .pyro: true
Try in Playground →

12. List Operations

Use map and filter with lambdas to transform and select list elements functionally.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Map: transform every element
squares = numbers |> map(|x| x * x)
print("Squares: {squares}")

# Filter: keep elements matching condition
odds = numbers |> filter(|x| x % 2 != 0)
print("Odds: {odds}")

# Chain: filter then map
result = numbers |> filter(|x| x >= 5)
                 |> map(|x| x * 10)
print(">=5 then *10: {result}")

# Sorting
names = ["Charlie", "Alice", "Bob"]
print("Sorted: {names.sort()}")
Squares: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] Odds: [1, 3, 5, 7, 9] >=5 then *10: [50, 60, 70, 80, 90, 100] Sorted: [Alice, Bob, Charlie]
Try in Playground →

13. Multiline Strings

Triple-quoted strings """...""" preserve newlines and indentation. Great for templates, SQL, and HTML.

# Multiline string
poem = """Roses are red,
Violets are blue,
Pyro is fast,
And elegant too."""
print(poem)

# Interpolation works inside triple-quotes
name = "Pyro"
version = 1.0
info = """
Language: {name}
Version:  {version}
Speed:    79x faster than Python
"""
print(info)
Roses are red, Violets are blue, Pyro is fast, And elegant too. Language: Pyro Version: 1.0 Speed: 79x faster than Python
Try in Playground →

14. Error Handling

Use try/catch for error handling and throw to raise errors. No colons.

fn divide(a, b)
    if b == 0
        throw "Division by zero!"
    return a / b

# Safe division
try
    result = divide(10, 3)
    print("10 / 3 = {result}")
catch e
    print("Error: {e}")

# This will throw
try
    result = divide(10, 0)
    print("Result: {result}")
catch e
    print("Caught: {e}")

print("Program continues after error!")
10 / 3 = 3.3333 Caught: Division by zero! Program continues after error!
Try in Playground →

15. Importing Modules

Pyro has 76 built-in modules. Import with import module — no pip install, no package managers.

import math
import time

# Use module functions with dot notation
print("Pi = {math.PI}")
print("sqrt(64) = {math.sqrt(64)}")

# Time module
now = time.now()
print("Timestamp: {now}")

# Benchmark something
start = time.now()
mut sum = 0
for i in 0..100000
    sum = sum + i
elapsed = time.now() - start
print("Sum of 0..100k = {sum} in {elapsed} ms")
Pi = 3.14159265358979 sqrt(64) = 8.0 Timestamp: 1710672000 Sum of 0..100k = 4999950000 in 2 ms
Try in Playground →

16. Math Module

Constants, square roots, powers, trig, and random numbers — all built in.

import math

# Constants
print("PI  = {math.PI}")
print("E   = {math.E}")
print("TAU = {math.TAU}")

# Functions
print("sqrt(144) = {math.sqrt(144)}")
print("pow(2,10) = {math.pow(2, 10)}")
print("abs(-42)  = {math.abs(-42)}")

# Trigonometry
print("sin(PI/2) = {math.sin(math.PI / 2)}")
print("cos(0)    = {math.cos(0)}")

# Random numbers
print("Random: {math.random()}")
print("Rand 1-100: {math.randint(1, 100)}")
PI = 3.14159265358979 E = 2.71828182845905 TAU = 6.28318530717959 sqrt(144) = 12.0 pow(2,10) = 1024.0 abs(-42) = 42 sin(PI/2) = 1.0 cos(0) = 1.0 Random: 0.7291 Rand 1-100: 42
Try in Playground →

17. JSON Parsing

Parse, stringify, and pretty-print JSON with the built-in json module.

import json

# Parse JSON string to object
raw = "{\"name\": \"Pyro\", \"speed\": 79, \"compiled\": true}"
data = json.parse(raw)
print("Name: {data[\"name\"]}")
print("Speed: {data[\"speed\"]}x")

# Stringify object to JSON
obj = {"lang": "Pyro", "version": 1.0}
print("JSON: {json.stringify(obj)}")

# Pretty print
print("Pretty:")
print(json.pretty(obj))

# Parse array
arr = json.parse("[1, 2, 3, 4, 5]")
print("Array: {arr}")
Name: Pyro Speed: 79x JSON: {"lang":"Pyro","version":1.0} Pretty: { "lang": "Pyro", "version": 1.0 } Array: [1, 2, 3, 4, 5]
Try in Playground →

18. File I/O

Read and write files with the io module. Simple one-liners for common operations.

import io

# Write a file
io.write("hello.txt", "Hello from Pyro!\nLine 2\nLine 3")
print("File written!")

# Read entire file
content = io.read("hello.txt")
print("Content: {content}")

# Read as lines
lines = io.readlines("hello.txt")
print("Lines: {lines}")
print("Line count: {lines.len()}")

# Append to file
io.append("hello.txt", "\nLine 4 appended!")
print(io.read("hello.txt"))
File written! Content: Hello from Pyro! Line 2 Line 3 Lines: [Hello from Pyro!, Line 2, Line 3] Line count: 3 Hello from Pyro! Line 2 Line 3 Line 4 appended!
Try in Playground →

19. Regular Expressions

Pattern matching with the re module — find, match, and replace text.

import re

text = "Call 555-1234 or 555-5678 today!"

# Find all matches
phones = re.find_all("\\d{3}-\\d{4}", text)
print("Phones: {phones}")

# Test if pattern matches
print("Has phone: {re.match(\"\\d{3}-\\d{4}\", text)}")
print("Has email: {re.match(\"@\", text)}")

# Replace with regex
cleaned = re.replace("\\d", text, "#")
print("Cleaned: {cleaned}")

# Extract emails
msg = "Contact us at hello@pyro.dev or support@pyro.dev"
emails = re.find_all("[\\w.]+@[\\w.]+", msg)
print("Emails: {emails}")
Phones: [555-1234, 555-5678] Has phone: true Has email: false Cleaned: Call ###-#### or ###-#### today! Emails: [hello@pyro.dev, support@pyro.dev]
Try in Playground →

20. Testing

Built-in test framework with test.run, test.eq, and test.summary. No pytest needed.

import test

# Define what we're testing
fn add(a, b)
    return a + b

fn factorial(n)
    if n <= 1
        return 1
    return n * factorial(n - 1)

# Run tests
test.run("addition", fn()
    test.eq(add(2, 3), 5)
    test.eq(add(-1, 1), 0)
    test.eq(add(0, 0), 0)
)

test.run("factorial", fn()
    test.eq(factorial(0), 1)
    test.eq(factorial(1), 1)
    test.eq(factorial(5), 120)
    test.eq(factorial(10), 3628800)
)

test.summary()
PASS addition (3 assertions) PASS factorial (4 assertions) All 2 tests passed (7 assertions)
Try in Playground →
Advanced

21. Web Server

Build a web server with routes and static files using the built-in web module. No Express, no Flask.

import web

# Create a web app
app = web.App()

# Define routes
app.get("/", fn(req, res)
    res.send("Hello from Pyro Web!")
)

app.get("/api/status", fn(req, res)
    res.json({"status": "ok", "lang": "Pyro"})
)

app.get("/greet/{name}", fn(req, res)
    res.send("Hello, {req.params.name}!")
)

# Serve static files
app.static("/public", "./static")

# Start server
app.listen(8080)
print("Server running on http://localhost:8080")
Server running on http://localhost:8080
Try in Playground →

22. Database

SQLite CRUD operations with the built-in db module. No ORM setup, no drivers.

import db

# Connect to SQLite (creates file if needed)
conn = db.connect("app.db")

# Create table
conn.exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")

# Insert rows
conn.exec("INSERT INTO users (name, age) VALUES ('Alice', 25)")
conn.exec("INSERT INTO users (name, age) VALUES ('Bob', 30)")
conn.exec("INSERT INTO users (name, age) VALUES ('Charlie', 22)")
print("Inserted 3 users")

# Query
rows = conn.query("SELECT * FROM users")
for row in rows
    print("{row}")

# Update
conn.exec("UPDATE users SET age = 26 WHERE name = 'Alice'")

# Count
count = conn.query("SELECT COUNT(*) as total FROM users")
print("Total users: {count}")

conn.close()
Inserted 3 users {id: 1, name: Alice, age: 25} {id: 2, name: Bob, age: 30} {id: 3, name: Charlie, age: 22} Total users: [{total: 3}]
Try in Playground →

23. HTTP Client

Make HTTP requests with the built-in http module. GET, POST, headers, JSON — all included.

import http

# Simple GET request
res = http.get("https://jsonplaceholder.typicode.com/posts/1")
print("Status: {res.status}")
print("Title: {res.json()[\"title\"]}")

# POST request with JSON body
res = http.post("https://jsonplaceholder.typicode.com/posts", {
    "title": "Pyro is awesome",
    "body": "Built-in HTTP client!",
    "userId": 1
})
print("Created post ID: {res.json()[\"id\"]}")

# GET with headers
res = http.get("https://api.github.com", {
    "User-Agent": "Pyro/1.0"
})
print("GitHub API status: {res.status}")
Status: 200 Title: sunt aut facere repellat provident occaecati Created post ID: 101 GitHub API status: 200
Try in Playground →

24. AI Chat

Built-in AI module for chat, translation, and summarization. Configure a provider first with ai.provider().

import ai

# Configure your AI provider (required)
ai.provider("openai", "sk-your-key")
# Or: ai.provider("ollama", "")  # local, no key needed
# Or: ai.provider("gemini", "your-key")

# Chat with AI
response = ai.chat("Explain Pyro in one sentence")
print("AI says: {response}")

# Translate text
spanish = ai.translate("Hello, how are you?", "Spanish")
print("Spanish: {spanish}")

# Summarize text
text = "Pyro is a compiled programming language created by Aravind Pilla. It compiles to native C++ and runs 79x faster than Python."
summary = ai.summarize(text)
print("Summary: {summary}")
AI says: Pyro is a compiled language that combines Python's simplicity with C++ speed. Spanish: Hola, como estas? Summary: Pyro: compiled lang, 79x faster than Python, 76 modules, 23 keywords.
Try in Playground →

25. Machine Learning

Train models with the built-in ml module. Linear regression, prediction, evaluation — no scikit-learn.

import ml

# Dataset: [hours_studied, sleep] -> exam_score
mut ds = ml.Dataset()
ds.X = [[1.0, 8.0], [2.0, 7.0], [3.0, 6.0], [5.0, 6.0], [7.0, 4.0], [8.0, 3.0]]
ds.y = [30.0, 40.0, 50.0, 65.0, 80.0, 85.0]

# Train linear regression
model = ml.linear_regression(ds, 0.01, 2000)
print(model)

# Predict new data
mut new_data = ml.Dataset()
new_data.X = [[4.0, 7.0], [9.0, 2.0]]
preds = model.predict(new_data)
print("Predictions: {preds}")

# Evaluate
print("MSE: {ml.mse(preds, [55.0, 90.0])}")
LinearRegression(weights=[7.82, -2.14], bias=18.5) Predictions: [34.42, 88.24] MSE: 213.45
Try in Playground →

26. NLP

Natural Language Processing built in. Sentiment analysis, tokenization, keyword extraction — no NLTK or spaCy.

import nlp

text = "Pyro is an amazing programming language. It compiles to C++ and runs incredibly fast."

# Sentiment analysis
print("Sentiment: {nlp.sentiment(text)}")
print("Negative:  {nlp.sentiment(\"This is terrible\")}")

# Tokenize
tokens = nlp.tokenize(text)
print("Tokens: {tokens}")

# Keywords (TF-IDF)
kw = nlp.keywords(text, 5)
print("Keywords: {kw}")

# Word count and sentence count
print("Words: {nlp.count_words(text)}")
print("Sentences: {nlp.count_sentences(text)}")

# Similarity
score = nlp.similarity("Pyro is fast", "Pyro has great speed")
print("Similarity: {score}")
Sentiment: {score: 0.85, label: positive} Negative: {score: -0.72, label: negative} Tokens: [Pyro, is, an, amazing, programming, language, It, compiles, to, C++, and, runs, incredibly, fast] Keywords: [Pyro, programming, compiles, language, fast] Words: 14 Sentences: 2 Similarity: 0.82
Try in Playground →

27. Visualization

Create charts with the viz module. Bar charts, pie charts, line graphs — rendered natively.

import viz

# Bar chart
viz.bar(
    "Programming Languages",
    ["Pyro", "Python", "Go", "Rust"],
    [95, 85, 78, 92]
)

# Pie chart
viz.pie(
    "Market Share",
    ["Chrome", "Firefox", "Safari", "Edge"],
    [65, 12, 18, 5]
)

# Line chart
viz.line(
    "Growth Over Time",
    [1, 2, 3, 4, 5],
    [10, 25, 45, 80, 150]
)

print("Charts rendered!")
Bar chart: Programming Languages (4 bars) Pie chart: Market Share (4 slices) Line chart: Growth Over Time (5 points) Charts rendered!
Try in Playground →

28. Benchmarking

Measure performance with test.bench. Compare different implementations side by side.

import test
import time

# Iterative fibonacci
fn fib_iter(n)
    mut a = 0
    mut b = 1
    for i in 0..n
        mut temp = b
        b = a + b
        a = temp
    return a

# Recursive fibonacci
fn fib_rec(n)
    if n <= 1
        return n
    return fib_rec(n - 1) + fib_rec(n - 2)

# Benchmark both
test.bench("fib_iter(30)", fn()
    fib_iter(30)
)

test.bench("fib_rec(30)", fn()
    fib_rec(30)
)

# Verify they give same result
print("Iterative: {fib_iter(30)}")
print("Recursive: {fib_rec(30)}")
BENCH fib_iter(30): 0.001 ms (1000 iterations) BENCH fib_rec(30): 12.4 ms (100 iterations) Iterative: 832040 Recursive: 832040
Try in Playground →

29. Environment Variables

Access env vars and load .env files with the built-in env module.

import env

# Get environment variable
home = env.get("HOME")
print("Home: {home}")

path = env.get("PATH")
print("Path: {path}")

# Get with default value
port = env.get("PORT", "3000")
print("Port: {port}")

# Load .env file
env.load(".env")
print("Loaded .env file")

# Set environment variable
env.set("APP_NAME", "PyroApp")
print("APP_NAME = {env.get(\"APP_NAME\")}")

# List all env vars
all_vars = env.keys()
print("Total env vars: {all_vars.len()}")
Home: /home/user Path: /usr/local/bin:/usr/bin Port: 3000 Loaded .env file APP_NAME = PyroApp Total env vars: 42
Try in Playground →

30. Form Validation

Validate user input with the validate module. Email, phone, URL, and custom rules.

import validate

# Email validation
print("Email valid: {validate.email(\"user@pyro.dev\")}")
print("Email valid: {validate.email(\"not-an-email\")}")

# Phone validation
print("Phone valid: {validate.phone(\"+1-555-123-4567\")}")
print("Phone valid: {validate.phone(\"abc\")}")

# URL validation
print("URL valid: {validate.url(\"https://pyro.dev\")}")
print("URL valid: {validate.url(\"not a url\")}")

# Validate a form
form = {
    "name": "Aravind",
    "email": "aravind@pyro.dev",
    "age": 20
}

result = validate.check(form, {
    "name": "required|min:2",
    "email": "required|email",
    "age": "required|min:18"
})

print("Form valid: {result.valid}")
print("Errors: {result.errors}")
Email valid: true Email valid: false Phone valid: true Phone valid: false URL valid: true URL valid: false Form valid: true Errors: []
Try in Playground →
Expert

31. Structs

Define custom data structures with struct. No type annotations needed. Access fields with dot notation.

# Define a struct
struct User
    name
    age
    active = true

# Create instances
user1 = User("Aravind", 20)
user2 = User("Pyro", 1, false)

print("Name: {user1.name}, Age: {user1.age}")
print("Active: {user1.active}")
print("User2: {user2.name}, active={user2.active}")

# Struct with methods
struct Point
    x
    y

fn distance(p1, p2)
    dx = p1.x - p2.x
    dy = p1.y - p2.y
    return math.sqrt(dx * dx + dy * dy)

import math
a = Point(0, 0)
b = Point(3, 4)
print("Distance: {distance(a, b)}")
Name: Aravind, Age: 20 Active: true User2: Pyro, active=false Distance: 5.0
Try in Playground →

32. Enums

Type-safe enumerations with enum. Use match for exhaustive pattern matching on enum values.

# Define an enum
enum Color
    Red
    Green
    Blue

# Use enum values
favorite = Color.Blue
print("Favorite: {favorite}")

# Pattern matching with match
fn describe(c)
    match c
        Color.Red -> return "warm"
        Color.Green -> return "natural"
        Color.Blue -> return "cool"

print("Red is {describe(Color.Red)}")
print("Blue is {describe(Color.Blue)}")

# Enum for state machines
enum Status
    Pending
    Active
    Done

task = Status.Pending
print("Task: {task}")
Favorite: Blue Red is warm Blue is cool Task: Pending
Try in Playground →

33. Timing & Performance

Measure execution time with the time module. Pyro compiles to native C++ for maximum speed.

import time

fn compute()
    mut sum = 0
    for i in 0..1000000
        sum = sum + i
    return sum

start = time.now()
result = compute()
elapsed = time.now() - start

print("Result: {result}")
print("Time: {elapsed} ms")
print("Native speed!")
Result: 499999500000 Time: 2 ms Native speed!
Try in Playground →

34. Message Passing

Use lists as message queues to pass data between functions.

# Simple message queue pattern
mut queue = []

fn produce(q)
    for i in 0..5
        q.push("msg-{i}")

fn consume(q)
    for msg in q
        print("Received: {msg}")

produce(queue)
consume(queue)
print("All messages processed")
Received: msg-0 Received: msg-1 Received: msg-2 Received: msg-3 Received: msg-4 All messages processed
Try in Playground →

35. Template Rendering

Render dynamic HTML templates with web.render(). Use {var} placeholders inside templates for server-side rendering.

import web

# HTML template with placeholders
template = """
<html>
<body>
  <h1>Welcome, {name}!</h1>
  <p>You have {count} notifications.</p>
</body>
</html>
"""

# Render with data
html = web.render(template, {
    "name": "Aravind",
    "count": 5
})
print(html)

# Use in a web route
mut app = web.app()
app.get("/", fn(req) = web.html(web.render(template, {
    "name": req.query("name") ?? "Guest",
    "count": 0
})))
print("Template rendered!")
<html> <body> <h1>Welcome, Aravind!</h1> <p>You have 5 notifications.</p> </body> </html> Template rendered!
Try in Playground →

36. REST API

Build a full CRUD API with database persistence. Pyro's web and db modules handle everything.

import web
import db
import json

# Setup database
conn = db.connect(":memory:")
conn.exec("CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT, done INTEGER)")
conn.exec("INSERT INTO todos (title, done) VALUES ('Learn Pyro', 0)")
conn.exec("INSERT INTO todos (title, done) VALUES ('Build API', 0)")

# REST routes
mut app = web.app()

# GET all todos
app.get("/api/todos", fn(req)
    rows = conn.query("SELECT * FROM todos")
    return web.json(json.stringify(rows))
)

# POST create todo
app.post("/api/todos", fn(req)
    data = json.parse(req.body)
    conn.exec("INSERT INTO todos (title, done) VALUES (?, 0)", [data["title"]])
    return web.json("{\"status\": \"created\"}")
)

print("REST API with CRUD operations")
print("GET  /api/todos    — list all")
print("POST /api/todos    — create")

rows = conn.query("SELECT * FROM todos")
for row in rows
    print(row)
REST API with CRUD operations GET /api/todos — list all POST /api/todos — create {id: 1, title: Learn Pyro, done: 0} {id: 2, title: Build API, done: 0}
Try in Playground →

37. CSV Processing

Read, filter, and transform CSV data with the built-in csv module. No pandas needed.

import csv

# Parse CSV from string
raw = "name,age,city\nAravind,20,Hyderabad\nPyro,1,Cloud\nAlice,30,NYC"
data = csv.parse(raw)

print("Headers: {data[0]}")
print("Rows: {data.len() - 1}")

# Print all rows
for i in 1..data.len()
    row = data[i]
    print("{row[0]} is {row[1]} from {row[2]}")

# Convert back to CSV string
output = csv.stringify(data)
print("CSV output:")
print(output)
Headers: [name, age, city] Rows: 3 Aravind is 20 from Hyderabad Pyro is 1 from Cloud Alice is 30 from NYC CSV output: name,age,city Aravind,20,Hyderabad Pyro,1,Cloud Alice,30,NYC
Try in Playground →

38. Password Hashing

Securely hash and verify passwords with the crypto module. Uses PBKDF2 under the hood via OpenSSL.

import crypto

# Hash a password
password = "super-secret-123"
hashed = crypto.hash_password(password, 10000)
print("Hash: {hashed}")

# Verify password
valid = crypto.verify_password(password, hashed)
print("Valid password: {valid}")

wrong = crypto.verify_password("wrong-password", hashed)
print("Wrong password: {wrong}")

# Other crypto utilities
token = crypto.random_token(32)
print("Token: {token}")

hash256 = crypto.hash_sha256("Pyro")
print("SHA256: {hash256}")
Hash: pbkdf2$10000$... Valid password: true Wrong password: false Token: a8f3c2d9e1b7... SHA256: 9f2a1c...
Try in Playground →

39. JWT Tokens

Create and verify JSON Web Tokens with the auth module. Perfect for API authentication.

import auth

# Create a JWT token
secret = "my-secret-key"
payload = {
    "user_id": 42,
    "name": "Aravind",
    "role": "admin"
}

token = auth.jwt_sign(payload, secret)
print("Token: {token}")

# Verify and decode
decoded = auth.jwt_verify(token, secret)
print("Decoded: {decoded}")
print("User: {decoded[\"name\"]}")
print("Role: {decoded[\"role\"]}")

# Invalid token check
try
    auth.jwt_verify("bad-token", secret)
catch e
    print("Invalid token: {e}")
Token: eyJhbGciOiJIUzI1NiJ9... Decoded: {user_id: 42, name: Aravind, role: admin} User: Aravind Role: admin Invalid token: JWT verification failed
Try in Playground →

40. Rate Limiting

Add rate limiting middleware to your web server. Protect APIs from abuse with built-in rate module.

import web
import rate

mut app = web.app()

# Rate limit: 100 requests per minute per IP
limiter = rate.limiter(100, 60000)
app.use(limiter)

# Protected API routes
app.get("/api/data", fn(req)
    return web.json("{\"data\": \"protected\"}")
)

# Stricter limit for auth endpoints
auth_limiter = rate.limiter(5, 60000)
app.post("/api/login", fn(req)
    return web.json("{\"status\": \"ok\"}")
)

print("Rate limiting configured:")
print("  /api/data  — 100 req/min")
print("  /api/login — 5 req/min")
print("Excess requests get 429 Too Many Requests")
Rate limiting configured: /api/data — 100 req/min /api/login — 5 req/min Excess requests get 429 Too Many Requests
Try in Playground →

41. File Upload

Handle file I/O with the io and fs modules. Read, write, copy, and manage files easily.

import io
import fs

# Write a file
io.write("/tmp/hello.txt", "Hello from Pyro!\nLine two.")
print("File written!")

# Read it back
content = io.read("/tmp/hello.txt")
print("Content: {content}")

# Read as lines
lines = io.readlines("/tmp/hello.txt")
print("Lines: {lines.len()}")
for line in lines
    print("  > {line}")

# Append to file
io.append("/tmp/hello.txt", "\nLine three!")

# File info
print("Exists: {fs.exists(\"/tmp/hello.txt\")}")
print("Size: {fs.size(\"/tmp/hello.txt\")} bytes")
File written! Content: Hello from Pyro! Line two. Lines: 2 > Hello from Pyro! > Line two. Exists: true Size: 32 bytes
Try in Playground →

42. Image Processing

Process images with the cv module. Create, transform, and analyze images — no OpenCV install needed.

import cv

# Create a 100x100 red image
img = cv.create(100, 100, 255, 0, 0)
print("Created 100x100 image")

# Draw shapes
cv.fill_rect(img, 10, 10, 30, 30, 0, 255, 0)
cv.draw_rect(img, 50, 50, 40, 40, 0, 0, 255)
print("Drew green filled rect and blue outline")

# Image operations
gray = cv.grayscale(img)
print("Converted to grayscale")

flipped = cv.flip(img, "horizontal")
print("Flipped horizontally")

resized = cv.resize(img, 50, 50)
print("Resized to 50x50")

edges = cv.edges(gray)
print("Edge detection applied")

# Save to file
# cv.save(img, "output.ppm")
print("Image processing complete!")
Created 100x100 image Drew green filled rect and blue outline Converted to grayscale Flipped horizontally Resized to 50x50 Edge detection applied Image processing complete!
Try in Playground →

43. Data Pipeline

Chain data transformations with the |> pipe operator. Build ETL pipelines that read like English.

# Raw sales data
sales = [
    {"product": "Widget", "price": 25.0, "qty": 10},
    {"product": "Gadget", "price": 50.0, "qty": 3},
    {"product": "Gizmo", "price": 15.0, "qty": 20},
    {"product": "Widget", "price": 25.0, "qty": 5}
]

# Pipeline: calculate totals with pipes + lambdas
totals = sales |> map(|s| s["price"] * s["qty"])
print("Order totals: {totals}")

# Filter big orders and sum
big_orders = totals |> filter(|t| t > 100)
print("Big orders (>$100): {big_orders}")

# Full pipeline in one chain
revenue = sales
    |> map(|s| s["price"] * s["qty"])
    |> filter(|t| t > 0)
print("All revenue: {revenue}")
print("Total: ${revenue.sum()}")
Order totals: [250.0, 150.0, 300.0, 125.0] Big orders (>$100): [250.0, 150.0, 300.0, 125.0] All revenue: [250.0, 150.0, 300.0, 125.0] Total: $825.0
Try in Playground →

44. Websocket Chat

Build real-time messaging with the websocket module. No Socket.IO or external libraries needed.

import web
import websocket

mut app = web.app()

# Create a WebSocket hub for chat
hub = websocket.hub()

# WebSocket endpoint
app.get("/ws", fn(req)
    ws = websocket.upgrade(req)
    hub.add(ws)

    ws.on("message", fn(msg)
        # Broadcast to all connected clients
        hub.broadcast("User says: {msg}")
    )

    ws.on("close", fn()
        hub.remove(ws)
    )
)

# Serve the chat page
app.get("/", fn(req) = web.html("<h1>Pyro Chat</h1>"))

print("WebSocket chat server")
print("Connect to ws://localhost:3000/ws")
print("Messages broadcast to all clients")
WebSocket chat server Connect to ws://localhost:3000/ws Messages broadcast to all clients
Try in Playground →

45. Cron Jobs

Schedule recurring tasks with time.every() and delayed execution with time.after(). No crontab needed.

import time

# Run a function every 2 seconds, 3 times
mut count = 0
time.every(2000, fn()
    count = count + 1
    print("Tick {count} at {time.timestamp()}")
, 3)

# Run once after a delay
time.after(1000, fn()
    print("Delayed task ran after 1 second")
)

# Timer for measuring performance
timer = time.timer()
mut sum = 0
for i in 0..100000
    sum = sum + i
print("Sum: {sum}")
print("Elapsed: {timer.elapsed()} ms")

print("Today: {time.today()}")
print("Now: {time.timestamp()}")
Delayed task ran after 1 second Tick 1 at 2026-03-17 10:00:02 Tick 2 at 2026-03-17 10:00:04 Tick 3 at 2026-03-17 10:00:06 Sum: 4999950000 Elapsed: 2 ms Today: 2026-03-17 Now: 2026-03-17 10:00:06
Try in Playground →

46. CLI Arguments

Build command-line tools with os.args(). Parse flags, options, and positional arguments.

import os

# Get command-line arguments
# Usage: pyro run cli.ro --name Aravind --verbose
args = os.args()
print("Arguments: {args}")
print("Count: {args.len()}")

# Simple flag parser
fn has_flag(args, flag)
    for arg in args
        if arg == flag
            return true
    return false

fn get_option(args, key)
    for i in 0..args.len()
        if args[i] == key
            if i + 1 < args.len()
                return args[i + 1]
    return nil

# Parse flags
verbose = has_flag(args, "--verbose")
name = get_option(args, "--name") ?? "World"

print("Hello, {name}!")
print("Verbose: {verbose}")
print("Platform: {os.platform()}")
print("CPUs: {os.cpus()}")
Arguments: [cli.ro] Count: 1 Hello, World! Verbose: false Platform: linux CPUs: 4
Try in Playground →

47. Unit Testing

Write comprehensive tests with test.run(). Assert equality, comparisons, truthiness, and expected failures.

import test

# Test a function
fn factorial(n)
    if n <= 1
        return 1
    return n * factorial(n - 1)

test.run("factorial base cases", fn()
    test.eq(factorial(0), 1)
    test.eq(factorial(1), 1)
)

test.run("factorial recursive", fn()
    test.eq(factorial(5), 120)
    test.eq(factorial(10), 3628800)
    test.gt(factorial(10), 1000)
)

# Test string operations
test.run("string basics", fn()
    name = "Pyro"
    test.eq(name.len(), 4)
    test.neq(name, "Python")
    test.ok(name.len() > 0)
)

# Test list operations
test.run("list operations", fn()
    nums = [1, 2, 3, 4, 5]
    test.eq(nums.len(), 5)
    test.eq(nums.sum(), 15)
    test.eq(nums.min(), 1)
    test.eq(nums.max(), 5)
)

test.summary()
PASS factorial base cases (2 assertions) PASS factorial recursive (3 assertions) PASS string basics (3 assertions) PASS list operations (4 assertions) 4 passed, 0 failed
Try in Playground →

48. Benchmarking

Measure performance with test.bench(). Compare algorithms and find bottlenecks with nanosecond precision.

import test
import time

# Iterative vs recursive fibonacci
fn fib_recursive(n)
    if n <= 1
        return n
    return fib_recursive(n - 1) + fib_recursive(n - 2)

fn fib_iterative(n)
    mut a = 0
    mut b = 1
    for i in 0..n
        temp = a + b
        a = b
        b = temp
    return a

# Verify correctness first
test.run("fib correctness", fn()
    test.eq(fib_recursive(10), 55)
    test.eq(fib_iterative(10), 55)
)

# Benchmark both approaches
test.bench("fib_recursive(20)", 100, fn() = fib_recursive(20))
test.bench("fib_iterative(20)", 100, fn() = fib_iterative(20))

# Benchmark string operations
test.bench("string concat x1000", 10, fn()
    mut s = ""
    for i in 0..1000
        s = s + "x"
)

test.summary()
PASS fib correctness (2 assertions) BENCH fib_recursive(20) 100 iters avg 0.12ms BENCH fib_iterative(20) 100 iters avg 0.001ms BENCH string concat x1000 10 iters avg 0.8ms 1 passed, 0 failed, 3 benchmarks
Try in Playground →

49. Error Types

Handle different error types with try/catch. Use throw for custom errors and ?? for nil coalescing.

# Custom error types
fn parse_int(s)
    if s == ""
        throw "EmptyInput: string is empty"
    for c in s
        if c < "0" || c > "9"
            throw "InvalidChar: '{c}' is not a digit"
    return s

# Catch specific errors
try
    result = parse_int("42")
    print("Parsed: {result}")
catch e
    print("Error: {e}")

try
    result = parse_int("12x")
catch e
    print("Caught: {e}")

try
    result = parse_int("")
catch e
    print("Caught: {e}")

# Nil coalescing with ??
config = nil
port = config ?? 3000
print("Port: {port}")

# try/catch/finally
try
    print("Opening resource...")
    throw "ConnectionFailed"
catch e
    print("Handling: {e}")
finally
    print("Cleanup done")
Parsed: 42 Caught: InvalidChar: 'x' is not a digit Caught: EmptyInput: string is empty Port: 3000 Opening resource... Handling: ConnectionFailed Cleanup done
Try in Playground →

50. Project Setup

Scaffold a new Pyro project with pyro init. Standard folder structure with config, tests, and entry point.

# Create a new project
# $ pyro init my-app
#
# Creates:
#   my-app/
#     main.ro          # entry point
#     pyro.toml         # project config
#     src/
#       lib.ro          # library code
#     tests/
#       test_main.ro    # test file
#     .gitignore

# main.ro — entry point
print("Welcome to my-app!")

# src/lib.ro — shared functions
fn add(a, b)
    return a + b

fn greet(name)
    return "Hello, {name}!"

# tests/test_main.ro
import test

test.run("add function", fn()
    test.eq(add(2, 3), 5)
    test.eq(add(0, 0), 0)
    test.eq(add(-1, 1), 0)
)

test.run("greet function", fn()
    test.eq(greet("World"), "Hello, World!")
    test.eq(greet("Pyro"), "Hello, Pyro!")
)

test.summary()

# Build and run:
# $ pyro run main.ro
# $ pyro build main.ro   # creates native binary
# $ pyro test             # runs all tests
Welcome to my-app! PASS add function (3 assertions) PASS greet function (2 assertions) 2 passed, 0 failed
Try in Playground →