From 52e3876b81c8760f9eb44b71b2d2de50a45e5c98 Mon Sep 17 00:00:00 2001 From: Brandon Lucas Date: Mon, 16 Feb 2026 23:34:17 -0500 Subject: [PATCH] feat: add projects showcase and Lux-powered static file server - Add website/serve.lux: static file server using HttpServer effect - Demonstrates serving the Lux website with Lux itself - Handles index files, clean URLs, and 404 responses - Add website/projects/index.html: example projects showcase - Features 6 real project cards (REST API, Todo App, JSON Parser, etc.) - Highlights Task Manager API demonstrating all 3 killer features - Links to full source code in the repository - Update examples sidebar with Projects section Co-Authored-By: Claude Opus 4.5 --- website/examples/index.html | 8 + website/projects/index.html | 456 ++++++++++++++++++++++++++++++++++++ website/serve.lux | 66 ++++++ 3 files changed, 530 insertions(+) create mode 100644 website/projects/index.html create mode 100644 website/serve.lux diff --git a/website/examples/index.html b/website/examples/index.html index a6ddd33..729d1ad 100644 --- a/website/examples/index.html +++ b/website/examples/index.html @@ -194,6 +194,14 @@
  • Middleware
  • Routing
  • + +

    Projects

    +
    diff --git a/website/projects/index.html b/website/projects/index.html new file mode 100644 index 0000000..496d98a --- /dev/null +++ b/website/projects/index.html @@ -0,0 +1,456 @@ + + + + + + Projects - Lux + + + + + + + + + + + +
    +
    +

    Example Projects

    +

    Real-world applications demonstrating Lux's unique capabilities. Clone these to learn by doing.

    +
    + +
    +
    +

    API REST API

    +

    + A full REST API for task management with JSON responses, routing, and CRUD operations. +

    +
    +

    Demonstrates

    +
      +
    • HttpServer effect
    • +
    • Pattern matching for routing
    • +
    • JSON serialization
    • +
    • Effect tracking
    • +
    +
    +
    +
    fn router(req): Response with {Http} =
    +    match (req.method, req.path) {
    +        ("GET", "/") => httpOk(welcome),
    +        ("GET", "/tasks") => httpOk(tasks),
    +        _ => httpNotFound("404")
    +    }
    +
    +
    + View Code +
    +
    + +
    +

    TODO Todo App

    +

    + A command-line todo application showcasing algebraic data types and pattern matching. +

    +
    +

    Demonstrates

    +
      +
    • ADTs for data modeling
    • +
    • Pattern matching
    • +
    • Recursive list operations
    • +
    • Console effect
    • +
    +
    +
    +
    type Priority =
    +    | Low
    +    | Medium
    +    | High
    +
    +type TodoItem =
    +    | TodoItem(Int, String, Bool, Priority)
    +
    +
    + View Code +
    +
    + +
    +

    JSON JSON Parser

    +

    + A recursive descent JSON parser demonstrating ADTs for AST representation. +

    +
    +

    Demonstrates

    +
      +
    • ADTs for AST
    • +
    • Recursive parsing
    • +
    • String manipulation
    • +
    • Pure functions
    • +
    +
    +
    +
    type JsonValue =
    +    | JsonNull
    +    | JsonBool(Bool)
    +    | JsonNumber(Int)
    +    | JsonString(String)
    +    | JsonArray(List<JsonValue>)
    +    | JsonObject(List<(String, JsonValue)>)
    +
    +
    + View Code +
    +
    + +
    +

    GAME Guessing Game

    +

    + A simple number guessing game demonstrating effects for randomness and I/O. +

    +
    +

    Demonstrates

    +
      +
    • Random effect
    • +
    • Console I/O
    • +
    • Recursive game loop
    • +
    • User input handling
    • +
    +
    +
    +
    fn playGame(): Unit
    +    with {Console, Random} = {
    +    let secret = Random.int(1, 100)
    +    Console.print("Guess a number!")
    +    guessLoop(secret, 0)
    +}
    +
    +
    + View Code +
    +
    + +
    +

    MD Markdown Converter

    +

    + Convert Markdown to HTML using pattern matching and string processing. +

    +
    +

    Demonstrates

    +
      +
    • String manipulation
    • +
    • Pattern matching
    • +
    • Pure transformation
    • +
    • List processing
    • +
    +
    +
    +
    fn convertLine(line: String): String =
    +    if startsWith(line, "# ") then
    +        "<h1>" + rest(line, 2) + "</h1>"
    +    else if startsWith(line, "- ") then
    +        "<li>" + rest(line, 2) + "</li>"
    +    else "<p>" + line + "</p>"
    +
    +
    + View Code +
    +
    + +
    +

    CALC Mini Interpreter

    +

    + A tiny expression interpreter demonstrating language implementation patterns. +

    +
    +

    Demonstrates

    +
      +
    • ADTs for AST
    • +
    • Recursive evaluation
    • +
    • Environment handling
    • +
    • Interpreter patterns
    • +
    +
    +
    +
    type Expr =
    +    | Num(Int)
    +    | Add(Expr, Expr)
    +    | Mul(Expr, Expr)
    +    | Var(String)
    +    | Let(String, Expr, Expr)
    +
    +
    + View Code +
    +
    +
    + + +
    +

    Featured: Task Manager API

    +
    +

    A Complete Showcase of Lux's Unique Features

    +

    This comprehensive example demonstrates all three of Lux's killer features working together.

    + +
    +
    +

    1. Algebraic Effects

    +

    Every side effect is explicit in function signatures. No hidden I/O.

    +
    +
    +

    2. Behavioral Types

    +

    Compile-time guarantees: is pure, is total, is idempotent.

    +
    +
    +

    3. Schema Evolution

    +

    Versioned types with automatic migration. Data structures evolve safely.

    +
    +
    + +
    +
    // Task v1: Original data model
    +type Task @v1 {
    +    id: String,
    +    title: String,
    +    done: Bool
    +}
    +
    +// Task v2: Added priority field with migration
    +type Task @v2 {
    +    id: String,
    +    title: String,
    +    done: Bool,
    +    priority: String,
    +
    +    // Old tasks get "medium" priority by default
    +    from @v1 = {
    +        id: old.id,
    +        title: old.title,
    +        done: old.done,
    +        priority: "medium"
    +    }
    +}
    +
    +// Pure, total, idempotent business logic
    +fn validateTitle(title: String): Result<String, String>
    +    is pure, total =
    +    if String.length(title) == 0 then
    +        Err("Title cannot be empty")
    +    else
    +        Ok(title)
    +
    + + +
    +
    + + +
    +

    Build Your Own

    +

    Ready to start building with Lux?

    + +
    +
    + + diff --git a/website/serve.lux b/website/serve.lux new file mode 100644 index 0000000..3a5fa0c --- /dev/null +++ b/website/serve.lux @@ -0,0 +1,66 @@ +// Static File Server for Lux Website +// +// Usage: lux website/serve.lux +// Then open http://localhost:8080 +// +// This demonstrates serving the Lux website using Lux's own HTTP server! + +fn main(): Unit with {HttpServer, Console, File} = { + let port = 8080 + let root = "website" + + HttpServer.listen(port) + Console.print("Lux website server running at http://localhost:" + toString(port)) + Console.print("Serving files from: " + root) + Console.print("Press Ctrl+C to stop") + Console.print("") + + serverLoop(root) +} + +fn serverLoop(root: String): Unit with {HttpServer, Console, File} = { + let req = HttpServer.accept() + + // Log request + Console.print(req.method + " " + req.path) + + // Only handle GET requests + if req.method != "GET" then { + HttpServer.respond(405, "Method Not Allowed") + serverLoop(root) + } else { + serveFile(root, req.path) + serverLoop(root) + } +} + +fn serveFile(root: String, reqPath: String): Unit with {HttpServer, Console, File} = { + // Determine file path + let path = if reqPath == "/" then "/index.html" else reqPath + let filePath = root + path + + // Try to serve the file + if File.exists(filePath) then { + let content = File.read(filePath) + HttpServer.respond(200, content) + } else { + // Try with .html extension for clean URLs + let htmlPath = filePath + ".html" + if File.exists(htmlPath) then { + let content = File.read(htmlPath) + HttpServer.respond(200, content) + } else { + // Try index.html for directory paths + let indexPath = filePath + "/index.html" + if File.exists(indexPath) then { + let content = File.read(indexPath) + HttpServer.respond(200, content) + } else { + Console.print(" -> 404 Not Found: " + filePath) + HttpServer.respond(404, "Not Found: " + reqPath) + } + } + } +} + +let output = run main() with {}