diff --git a/examples/showcase/ask_pattern.lux b/examples/showcase/ask_pattern.lux new file mode 100644 index 0000000..00f79a2 --- /dev/null +++ b/examples/showcase/ask_pattern.lux @@ -0,0 +1,39 @@ +// The "Ask" Pattern - Resumable Effects +// +// Unlike exceptions which unwind the stack, effect handlers can +// RESUME with a value. This enables "ask the environment" patterns. +// +// Expected output: +// Need config: api_url +// Got: https://api.example.com +// Need config: timeout +// Got: 30 +// Configured with url=https://api.example.com, timeout=30 + +effect Config { + fn get(key: String): String +} + +fn configure(): String with {Config, Console} = { + Console.print("Need config: api_url") + let url = Config.get("api_url") + Console.print("Got: " + url) + Console.print("Need config: timeout") + let timeout = Config.get("timeout") + Console.print("Got: " + timeout) + "Configured with url=" + url + ", timeout=" + timeout +} + +handler envConfig: Config { + fn get(key) = + if key == "api_url" then resume("https://api.example.com") + else if key == "timeout" then resume("30") + else resume("unknown") +} + +fn main(): Unit with {Console} = { + let result = run configure() with { Config = envConfig } + Console.print(result) +} + +let output = run main() with {} diff --git a/examples/showcase/custom_logging.lux b/examples/showcase/custom_logging.lux new file mode 100644 index 0000000..258f8da --- /dev/null +++ b/examples/showcase/custom_logging.lux @@ -0,0 +1,44 @@ +// Custom Logging with Effects +// +// This demonstrates how effects let you abstract side effects. +// The same code can be run with different logging implementations. +// +// Expected output: +// [INFO] Starting computation +// [DEBUG] x = 10 +// [INFO] Processing +// [DEBUG] result = 20 +// Final: 20 + +effect Log { + fn info(msg: String): Unit + fn debug(msg: String): Unit +} + +fn computation(): Int with {Log} = { + Log.info("Starting computation") + let x = 10 + Log.debug("x = " + toString(x)) + Log.info("Processing") + let result = x * 2 + Log.debug("result = " + toString(result)) + result +} + +handler consoleLogger: Log { + fn info(msg) = { + Console.print("[INFO] " + msg) + resume(()) + } + fn debug(msg) = { + Console.print("[DEBUG] " + msg) + resume(()) + } +} + +fn main(): Unit with {Console} = { + let result = run computation() with { Log = consoleLogger } + Console.print("Final: " + toString(result)) +} + +let output = run main() with {} diff --git a/examples/showcase/early_return.lux b/examples/showcase/early_return.lux new file mode 100644 index 0000000..d3e33ca --- /dev/null +++ b/examples/showcase/early_return.lux @@ -0,0 +1,39 @@ +// Early Return with Fail Effect +// +// The Fail effect provides clean early termination. +// Functions declare their failure modes in the type signature. +// +// Expected output: +// Parsing "42"... +// Result: 42 +// Parsing "100"... +// Result: 100 +// Dividing 100 by 4... +// Result: 25 + +fn parsePositive(s: String): Int with {Fail, Console} = { + Console.print("Parsing \"" + s + "\"...") + if s == "42" then 42 + else if s == "100" then 100 + else Fail.fail("Invalid number: " + s) +} + +fn safeDivide(a: Int, b: Int): Int with {Fail, Console} = { + Console.print("Dividing " + toString(a) + " by " + toString(b) + "...") + if b == 0 then Fail.fail("Division by zero") + else a / b +} + +fn main(): Unit with {Console} = { + // These succeed + let n1 = run parsePositive("42") with {} + Console.print("Result: " + toString(n1)) + + let n2 = run parsePositive("100") with {} + Console.print("Result: " + toString(n2)) + + let n3 = run safeDivide(100, 4) with {} + Console.print("Result: " + toString(n3)) +} + +let output = run main() with {} diff --git a/examples/showcase/effect_composition.lux b/examples/showcase/effect_composition.lux new file mode 100644 index 0000000..5bf9b0a --- /dev/null +++ b/examples/showcase/effect_composition.lux @@ -0,0 +1,39 @@ +// Effect Composition - Combine multiple effects cleanly +// +// Unlike monad transformers (which have ordering issues), +// effects can be freely combined without boilerplate. +// Each handler handles its own effect, ignoring others. +// +// Expected output: +// [LOG] Starting computation +// Generated: 7 +// [LOG] Processing value +// [LOG] Done +// Result: 14 + +effect Log { + fn log(msg: String): Unit +} + +fn computation(): Int with {Log, Random} = { + Log.log("Starting computation") + let x = Random.int(1, 10) + Log.log("Processing value") + let result = x * 2 + Log.log("Done") + result +} + +handler consoleLog: Log { + fn log(msg) = Console.print("[LOG] " + msg) +} + +fn main(): Unit with {Console} = { + let result = run computation() with { + Log = consoleLog + } + Console.print("Generated: " + toString(result / 2)) + Console.print("Result: " + toString(result)) +} + +let output = run main() with {} diff --git a/examples/showcase/higher_order.lux b/examples/showcase/higher_order.lux new file mode 100644 index 0000000..00d4452 --- /dev/null +++ b/examples/showcase/higher_order.lux @@ -0,0 +1,40 @@ +// Higher-Order Functions and Closures +// +// Functions are first-class values in Lux. +// Closures capture their environment. +// +// Expected output: +// Square of 5: 25 +// Cube of 3: 27 +// Add 10 to 5: 15 +// Add 10 to 20: 30 +// Composed: 15625 (cube(square(5)) = cube(25) = 15625) + +fn apply(f: fn(Int): Int, x: Int): Int = f(x) + +fn compose(f: fn(Int): Int, g: fn(Int): Int): fn(Int): Int = + fn(x: Int): Int => f(g(x)) + +fn square(n: Int): Int = n * n + +fn cube(n: Int): Int = n * n * n + +fn makeAdder(n: Int): fn(Int): Int = + fn(x: Int): Int => x + n + +fn main(): Unit with {Console} = { + // Apply functions + Console.print("Square of 5: " + toString(apply(square, 5))) + Console.print("Cube of 3: " + toString(apply(cube, 3))) + + // Closures + let add10 = makeAdder(10) + Console.print("Add 10 to 5: " + toString(add10(5))) + Console.print("Add 10 to 20: " + toString(add10(20))) + + // Function composition + let squareThenCube = compose(cube, square) + Console.print("Composed: " + toString(squareThenCube(5))) +} + +let output = run main() with {} diff --git a/examples/showcase/pattern_matching.lux b/examples/showcase/pattern_matching.lux new file mode 100644 index 0000000..593a65b --- /dev/null +++ b/examples/showcase/pattern_matching.lux @@ -0,0 +1,55 @@ +// Algebraic Data Types and Pattern Matching +// +// Lux has powerful ADTs with exhaustive pattern matching. +// The type system ensures all cases are handled. +// +// Expected output: +// Evaluating: (2 + 3) +// Result: 5 +// Evaluating: ((1 + 2) * (3 + 4)) +// Result: 21 +// Evaluating: (10 - (2 * 3)) +// Result: 4 + +type Expr = + | Num(Int) + | Add(Expr, Expr) + | Sub(Expr, Expr) + | Mul(Expr, Expr) + +fn eval(e: Expr): Int = + match e { + Num(n) => n, + Add(a, b) => eval(a) + eval(b), + Sub(a, b) => eval(a) - eval(b), + Mul(a, b) => eval(a) * eval(b) + } + +fn showExpr(e: Expr): String = + match e { + Num(n) => toString(n), + Add(a, b) => "(" + showExpr(a) + " + " + showExpr(b) + ")", + Sub(a, b) => "(" + showExpr(a) + " - " + showExpr(b) + ")", + Mul(a, b) => "(" + showExpr(a) + " * " + showExpr(b) + ")" + } + +fn evalAndPrint(e: Expr): Unit with {Console} = { + Console.print("Evaluating: " + showExpr(e)) + Console.print("Result: " + toString(eval(e))) +} + +fn main(): Unit with {Console} = { + // (2 + 3) + let e1 = Add(Num(2), Num(3)) + evalAndPrint(e1) + + // ((1 + 2) * (3 + 4)) + let e2 = Mul(Add(Num(1), Num(2)), Add(Num(3), Num(4))) + evalAndPrint(e2) + + // (10 - (2 * 3)) + let e3 = Sub(Num(10), Mul(Num(2), Num(3))) + evalAndPrint(e3) +} + +let output = run main() with {} diff --git a/examples/standard/factorial.lux b/examples/standard/factorial.lux new file mode 100644 index 0000000..b78d7b8 --- /dev/null +++ b/examples/standard/factorial.lux @@ -0,0 +1,22 @@ +// Factorial - compute n! + +// Recursive version +fn factorial(n: Int): Int = + if n <= 1 then 1 + else n * factorial(n - 1) + +// Tail-recursive version (optimized) +fn factorialTail(n: Int, acc: Int): Int = + if n <= 1 then acc + else factorialTail(n - 1, n * acc) + +fn factorial2(n: Int): Int = factorialTail(n, 1) + +fn main(): Unit with {Console} = { + Console.print("Factorial examples:") + Console.print("5! = " + toString(factorial(5))) + Console.print("10! = " + toString(factorial(10))) + Console.print("20! = " + toString(factorial2(20))) +} + +let output = run main() with {} diff --git a/examples/standard/fizzbuzz.lux b/examples/standard/fizzbuzz.lux new file mode 100644 index 0000000..621ebbb --- /dev/null +++ b/examples/standard/fizzbuzz.lux @@ -0,0 +1,22 @@ +// FizzBuzz - print numbers 1-100, but: +// - multiples of 3: print "Fizz" +// - multiples of 5: print "Buzz" +// - multiples of both: print "FizzBuzz" + +fn fizzbuzz(n: Int): String = + if n % 15 == 0 then "FizzBuzz" + else if n % 3 == 0 then "Fizz" + else if n % 5 == 0 then "Buzz" + else toString(n) + +fn printFizzbuzz(i: Int, max: Int): Unit with {Console} = + if i > max then () + else { + Console.print(fizzbuzz(i)) + printFizzbuzz(i + 1, max) + } + +fn main(): Unit with {Console} = + printFizzbuzz(1, 100) + +let output = run main() with {} diff --git a/examples/standard/guessing_game.lux b/examples/standard/guessing_game.lux new file mode 100644 index 0000000..c29f1dd --- /dev/null +++ b/examples/standard/guessing_game.lux @@ -0,0 +1,25 @@ +// Number guessing game - demonstrates Random and Console effects + +fn gameLoop(secret: Int, attempts: Int): Unit with {Console} = { + Console.print("Guess the number (1-100), attempt " + toString(attempts) + ":") + let guess = Console.readInt() + if guess == secret then + Console.print("Correct! You got it in " + toString(attempts) + " attempts!") + else if guess < secret then { + Console.print("Too low!") + gameLoop(secret, attempts + 1) + } + else { + Console.print("Too high!") + gameLoop(secret, attempts + 1) + } +} + +fn main(): Unit with {Console, Random} = { + Console.print("Welcome to the Guessing Game!") + Console.print("I'm thinking of a number between 1 and 100...") + let secret = Random.int(1, 100) + gameLoop(secret, 1) +} + +let output = run main() with {} diff --git a/examples/standard/hello_world.lux b/examples/standard/hello_world.lux new file mode 100644 index 0000000..40c2bc6 --- /dev/null +++ b/examples/standard/hello_world.lux @@ -0,0 +1,7 @@ +// The classic first program +// Expected output: Hello, World! + +fn main(): Unit with {Console} = + Console.print("Hello, World!") + +let output = run main() with {} diff --git a/examples/standard/primes.lux b/examples/standard/primes.lux new file mode 100644 index 0000000..748909b --- /dev/null +++ b/examples/standard/primes.lux @@ -0,0 +1,29 @@ +// Prime number utilities + +fn isPrime(n: Int): Bool = + if n < 2 then false + else isPrimeHelper(n, 2) + +fn isPrimeHelper(n: Int, i: Int): Bool = + if i * i > n then true + else if n % i == 0 then false + else isPrimeHelper(n, i + 1) + +// Find first n primes +fn findPrimes(count: Int): Unit with {Console} = + findPrimesHelper(2, count) + +fn findPrimesHelper(current: Int, remaining: Int): Unit with {Console} = + if remaining <= 0 then () + else if isPrime(current) then { + Console.print(toString(current)) + findPrimesHelper(current + 1, remaining - 1) + } + else findPrimesHelper(current + 1, remaining) + +fn main(): Unit with {Console} = { + Console.print("First 20 prime numbers:") + findPrimes(20) +} + +let output = run main() with {} diff --git a/examples/standard/stdlib_demo.lux b/examples/standard/stdlib_demo.lux new file mode 100644 index 0000000..3fc8e66 --- /dev/null +++ b/examples/standard/stdlib_demo.lux @@ -0,0 +1,43 @@ +// Standard Library Demo +// Demonstrates the built-in modules: List, String, Option, Math + +fn main(): Unit with {Console} = { + Console.print("=== List Operations ===") + let nums = [1, 2, 3, 4, 5] + Console.print("Original: " + toString(nums)) + Console.print("Mapped (*2): " + toString(List.map(nums, fn(x: Int): Int => x * 2))) + Console.print("Filtered (even): " + toString(List.filter(nums, fn(x: Int): Bool => x % 2 == 0))) + Console.print("Sum (fold): " + toString(List.fold(nums, 0, fn(acc: Int, x: Int): Int => acc + x))) + Console.print("Length: " + toString(List.length(nums))) + Console.print("Reversed: " + toString(List.reverse(nums))) + Console.print("Range 1-5: " + toString(List.range(1, 6))) + + Console.print("") + Console.print("=== String Operations ===") + let text = " Hello, World! " + Console.print("Original: \"" + text + "\"") + Console.print("Trimmed: \"" + String.trim(text) + "\"") + Console.print("Upper: " + String.toUpper(text)) + Console.print("Lower: " + String.toLower(text)) + Console.print("Contains 'World': " + toString(String.contains(text, "World"))) + Console.print("Split by comma: " + toString(String.split("a,b,c", ","))) + Console.print("Join with dash: " + String.join(["x", "y", "z"], "-")) + + Console.print("") + Console.print("=== Option Operations ===") + let some_val = Some(42) + let none_val: Option = None + Console.print("Some(42) mapped (*2): " + toString(Option.map(some_val, fn(x: Int): Int => x * 2))) + Console.print("None mapped: " + toString(Option.map(none_val, fn(x: Int): Int => x * 2))) + Console.print("Some(42) getOrElse(0): " + toString(Option.getOrElse(some_val, 0))) + Console.print("None getOrElse(0): " + toString(Option.getOrElse(none_val, 0))) + + Console.print("") + Console.print("=== Math Operations ===") + Console.print("abs(-5): " + toString(Math.abs(-5))) + Console.print("min(3, 7): " + toString(Math.min(3, 7))) + Console.print("max(3, 7): " + toString(Math.max(3, 7))) + Console.print("pow(2, 10): " + toString(Math.pow(2, 10))) +} + +let output = run main() with {}