style: auto-format example files with lux fmt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,36 +1,19 @@
|
|||||||
// Demonstrating behavioral properties in Lux
|
fn add(a: Int, b: Int): Int is pure = a + b
|
||||||
// Behavioral properties are compile-time guarantees about function behavior
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// add(5, 3) = 8
|
|
||||||
// factorial(5) = 120
|
|
||||||
// multiply(7, 6) = 42
|
|
||||||
// abs(-5) = 5
|
|
||||||
|
|
||||||
// A pure function - no side effects, same input always gives same output
|
fn factorial(n: Int): Int is deterministic = if n <= 1 then 1 else n * factorial(n - 1)
|
||||||
fn add(a: Int, b: Int): Int is pure =
|
|
||||||
a + b
|
|
||||||
|
|
||||||
// A deterministic function - same input always gives same output
|
fn multiply(a: Int, b: Int): Int is commutative = a * b
|
||||||
fn factorial(n: Int): Int is deterministic =
|
|
||||||
if n <= 1 then 1
|
|
||||||
else n * factorial(n - 1)
|
|
||||||
|
|
||||||
// A commutative function - order of arguments doesn't matter
|
fn abs(x: Int): Int is idempotent = if x < 0 then 0 - x else x
|
||||||
fn multiply(a: Int, b: Int): Int is commutative =
|
|
||||||
a * b
|
|
||||||
|
|
||||||
// An idempotent function - absolute value
|
|
||||||
fn abs(x: Int): Int is idempotent =
|
|
||||||
if x < 0 then 0 - x else x
|
|
||||||
|
|
||||||
// Test the functions
|
|
||||||
let sumResult = add(5, 3)
|
let sumResult = add(5, 3)
|
||||||
|
|
||||||
let factResult = factorial(5)
|
let factResult = factorial(5)
|
||||||
|
|
||||||
let productResult = multiply(7, 6)
|
let productResult = multiply(7, 6)
|
||||||
|
|
||||||
let absResult = abs(0 - 5)
|
let absResult = abs(0 - 5)
|
||||||
|
|
||||||
// Print results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("add(5, 3) = " + toString(sumResult))
|
Console.print("add(5, 3) = " + toString(sumResult))
|
||||||
Console.print("factorial(5) = " + toString(factResult))
|
Console.print("factorial(5) = " + toString(factResult))
|
||||||
|
|||||||
@@ -1,82 +1,42 @@
|
|||||||
// Behavioral Types Demo
|
|
||||||
// Demonstrates compile-time verification of function properties
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// PART 1: Pure Functions
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Pure functions have no side effects
|
|
||||||
fn add(a: Int, b: Int): Int is pure = a + b
|
fn add(a: Int, b: Int): Int is pure = a + b
|
||||||
|
|
||||||
fn subtract(a: Int, b: Int): Int is pure = a - b
|
fn subtract(a: Int, b: Int): Int is pure = a - b
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// PART 2: Commutative Functions
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Commutative functions: f(a, b) = f(b, a)
|
|
||||||
fn multiply(a: Int, b: Int): Int is commutative = a * b
|
fn multiply(a: Int, b: Int): Int is commutative = a * b
|
||||||
|
|
||||||
fn sum(a: Int, b: Int): Int is commutative = a + b
|
fn sum(a: Int, b: Int): Int is commutative = a + b
|
||||||
|
|
||||||
// ============================================================
|
fn abs(x: Int): Int is idempotent = if x < 0 then 0 - x else x
|
||||||
// PART 3: Idempotent Functions
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Idempotent functions: f(f(x)) = f(x)
|
|
||||||
fn abs(x: Int): Int is idempotent =
|
|
||||||
if x < 0 then 0 - x else x
|
|
||||||
|
|
||||||
fn identity(x: Int): Int is idempotent = x
|
fn identity(x: Int): Int is idempotent = x
|
||||||
|
|
||||||
// ============================================================
|
fn factorial(n: Int): Int is deterministic = if n <= 1 then 1 else n * factorial(n - 1)
|
||||||
// PART 4: Deterministic Functions
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Deterministic functions always produce the same output for the same input
|
fn fib(n: Int): Int is deterministic = if n <= 1 then n else fib(n - 1) + fib(n - 2)
|
||||||
fn factorial(n: Int): Int is deterministic =
|
|
||||||
if n <= 1 then 1 else n * factorial(n - 1)
|
|
||||||
|
|
||||||
fn fib(n: Int): Int is deterministic =
|
fn sumTo(n: Int): Int is total = if n <= 0 then 0 else n + sumTo(n - 1)
|
||||||
if n <= 1 then n else fib(n - 1) + fib(n - 2)
|
|
||||||
|
|
||||||
// ============================================================
|
fn power(base: Int, exp: Int): Int is total = if exp <= 0 then 1 else base * power(base, exp - 1)
|
||||||
// PART 5: Total Functions
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Total functions are defined for all inputs (no infinite loops, no exceptions)
|
|
||||||
fn sumTo(n: Int): Int is total =
|
|
||||||
if n <= 0 then 0 else n + sumTo(n - 1)
|
|
||||||
|
|
||||||
fn power(base: Int, exp: Int): Int is total =
|
|
||||||
if exp <= 0 then 1 else base * power(base, exp - 1)
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// RESULTS
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== Behavioral Types Demo ===")
|
Console.print("=== Behavioral Types Demo ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
Console.print("Part 1: Pure functions")
|
Console.print("Part 1: Pure functions")
|
||||||
Console.print(" add(5, 3) = " + toString(add(5, 3)))
|
Console.print(" add(5, 3) = " + toString(add(5, 3)))
|
||||||
Console.print(" subtract(10, 4) = " + toString(subtract(10, 4)))
|
Console.print(" subtract(10, 4) = " + toString(subtract(10, 4)))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
Console.print("Part 2: Commutative functions")
|
Console.print("Part 2: Commutative functions")
|
||||||
Console.print(" multiply(7, 6) = " + toString(multiply(7, 6)))
|
Console.print(" multiply(7, 6) = " + toString(multiply(7, 6)))
|
||||||
Console.print(" sum(10, 20) = " + toString(sum(10, 20)))
|
Console.print(" sum(10, 20) = " + toString(sum(10, 20)))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
Console.print("Part 3: Idempotent functions")
|
Console.print("Part 3: Idempotent functions")
|
||||||
Console.print(" abs(-42) = " + toString(abs(0 - 42)))
|
Console.print(" abs(-42) = " + toString(abs(0 - 42)))
|
||||||
Console.print(" identity(100) = " + toString(identity(100)))
|
Console.print(" identity(100) = " + toString(identity(100)))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
Console.print("Part 4: Deterministic functions")
|
Console.print("Part 4: Deterministic functions")
|
||||||
Console.print(" factorial(5) = " + toString(factorial(5)))
|
Console.print(" factorial(5) = " + toString(factorial(5)))
|
||||||
Console.print(" fib(10) = " + toString(fib(10)))
|
Console.print(" fib(10) = " + toString(fib(10)))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
Console.print("Part 5: Total functions")
|
Console.print("Part 5: Total functions")
|
||||||
Console.print(" sumTo(10) = " + toString(sumTo(10)))
|
Console.print(" sumTo(10) = " + toString(sumTo(10)))
|
||||||
Console.print(" power(2, 8) = " + toString(power(2, 8)))
|
Console.print(" power(2, 8) = " + toString(power(2, 8)))
|
||||||
|
|||||||
@@ -1,31 +1,7 @@
|
|||||||
// Demonstrating built-in effects in Lux
|
fn safeDivide(a: Int, b: Int): Int with {Fail} = if b == 0 then Fail.fail("Division by zero") else a / b
|
||||||
//
|
|
||||||
// Lux provides several built-in effects:
|
|
||||||
// - Console: print and read from terminal
|
|
||||||
// - Fail: early termination with error
|
|
||||||
// - State: get/put mutable state (requires runtime initialization)
|
|
||||||
// - Reader: read-only environment access (requires runtime initialization)
|
|
||||||
//
|
|
||||||
// This example demonstrates Console and Fail effects.
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// Starting computation...
|
|
||||||
// Step 1: validating input
|
|
||||||
// Step 2: processing
|
|
||||||
// Result: 42
|
|
||||||
// Done!
|
|
||||||
|
|
||||||
// A function that can fail
|
fn validatePositive(n: Int): Int with {Fail} = if n < 0 then Fail.fail("Negative number not allowed") else n
|
||||||
fn safeDivide(a: Int, b: Int): Int with {Fail} =
|
|
||||||
if b == 0 then Fail.fail("Division by zero")
|
|
||||||
else a / b
|
|
||||||
|
|
||||||
// A function that validates input
|
|
||||||
fn validatePositive(n: Int): Int with {Fail} =
|
|
||||||
if n < 0 then Fail.fail("Negative number not allowed")
|
|
||||||
else n
|
|
||||||
|
|
||||||
// A computation that uses multiple effects
|
|
||||||
fn compute(input: Int): Int with {Console, Fail} = {
|
fn compute(input: Int): Int with {Console, Fail} = {
|
||||||
Console.print("Starting computation...")
|
Console.print("Starting computation...")
|
||||||
Console.print("Step 1: validating input")
|
Console.print("Step 1: validating input")
|
||||||
@@ -36,7 +12,6 @@ fn compute(input: Int): Int with {Console, Fail} = {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main function
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let result = run compute(21) with {}
|
let result = run compute(21) with {}
|
||||||
Console.print("Done!")
|
Console.print("Done!")
|
||||||
|
|||||||
@@ -1,14 +1,3 @@
|
|||||||
// Counter Example - A simple interactive counter using TEA pattern
|
|
||||||
//
|
|
||||||
// This example demonstrates:
|
|
||||||
// - Model-View-Update architecture (TEA)
|
|
||||||
// - Html DSL for describing UI (inline version)
|
|
||||||
// - Message-based state updates
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Html Types (subset of stdlib/html)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
type Html<M> =
|
type Html<M> =
|
||||||
| Element(String, List<Attr<M>>, List<Html<M>>)
|
| Element(String, List<Attr<M>>, List<Html<M>>)
|
||||||
| Text(String)
|
| Text(String)
|
||||||
@@ -19,86 +8,56 @@ type Attr<M> =
|
|||||||
| Id(String)
|
| Id(String)
|
||||||
| OnClick(M)
|
| OnClick(M)
|
||||||
|
|
||||||
// Html builder helpers
|
fn div<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = Element("div", attrs, children)
|
||||||
fn div<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
|
||||||
Element("div", attrs, children)
|
|
||||||
|
|
||||||
fn span<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
fn span<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = Element("span", attrs, children)
|
||||||
Element("span", attrs, children)
|
|
||||||
|
|
||||||
fn h1<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
fn h1<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = Element("h1", attrs, children)
|
||||||
Element("h1", attrs, children)
|
|
||||||
|
|
||||||
fn button<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
fn button<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = Element("button", attrs, children)
|
||||||
Element("button", attrs, children)
|
|
||||||
|
|
||||||
fn text<M>(content: String): Html<M> =
|
fn text<M>(content: String): Html<M> = Text(content)
|
||||||
Text(content)
|
|
||||||
|
|
||||||
fn class<M>(name: String): Attr<M> =
|
fn class<M>(name: String): Attr<M> = Class(name)
|
||||||
Class(name)
|
|
||||||
|
|
||||||
fn onClick<M>(msg: M): Attr<M> =
|
fn onClick<M>(msg: M): Attr<M> = OnClick(msg)
|
||||||
OnClick(msg)
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Model - The application state (using ADT wrapper)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
type Model =
|
type Model =
|
||||||
| Counter(Int)
|
| Counter(Int)
|
||||||
|
|
||||||
fn getCount(model: Model): Int =
|
fn getCount(model: Model): Int =
|
||||||
match model {
|
match model {
|
||||||
Counter(n) => n
|
Counter(n) => n,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(): Model = Counter(0)
|
fn init(): Model = Counter(0)
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Messages - Events that can occur
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
type Msg =
|
type Msg =
|
||||||
| Increment
|
| Increment
|
||||||
| Decrement
|
| Decrement
|
||||||
| Reset
|
| Reset
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Update - State transitions
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn update(model: Model, msg: Msg): Model =
|
fn update(model: Model, msg: Msg): Model =
|
||||||
match msg {
|
match msg {
|
||||||
Increment => Counter(getCount(model) + 1),
|
Increment => Counter(getCount(model) + 1),
|
||||||
Decrement => Counter(getCount(model) - 1),
|
Decrement => Counter(getCount(model) - 1),
|
||||||
Reset => Counter(0)
|
Reset => Counter(0),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// View - Render the UI
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn viewCounter(count: Int): Html<Msg> = {
|
fn viewCounter(count: Int): Html<Msg> = {
|
||||||
let countText = text(toString(count))
|
let countText = text(toString(count))
|
||||||
let countSpan = span([class("count")], [countText])
|
let countSpan = span([class("count")], [countText])
|
||||||
let displayDiv = div([class("counter-display")], [countSpan])
|
let displayDiv = div([class("counter-display")], [countSpan])
|
||||||
|
|
||||||
let minusBtn = button([onClick(Decrement), class("btn")], [text("-")])
|
let minusBtn = button([onClick(Decrement), class("btn")], [text("-")])
|
||||||
let resetBtn = button([onClick(Reset), class("btn btn-reset")], [text("Reset")])
|
let resetBtn = button([onClick(Reset), class("btn btn-reset")], [text("Reset")])
|
||||||
let plusBtn = button([onClick(Increment), class("btn")], [text("+")])
|
let plusBtn = button([onClick(Increment), class("btn")], [text("+")])
|
||||||
let buttonsDiv = div([class("counter-buttons")], [minusBtn, resetBtn, plusBtn])
|
let buttonsDiv = div([class("counter-buttons")], [minusBtn, resetBtn, plusBtn])
|
||||||
|
|
||||||
let title = h1([], [text("Counter")])
|
let title = h1([], [text("Counter")])
|
||||||
div([class("counter-app")], [title, displayDiv, buttonsDiv])
|
div([class("counter-app")], [title, displayDiv, buttonsDiv])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(model: Model): Html<Msg> = viewCounter(getCount(model))
|
fn view(model: Model): Html<Msg> = viewCounter(getCount(model))
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Debug: Print Html structure
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn showAttr(attr: Attr<Msg>): String =
|
fn showAttr(attr: Attr<Msg>): String =
|
||||||
match attr {
|
match attr {
|
||||||
Class(s) => "class=\"" + s + "\"",
|
Class(s) => "class=\"" + s + "\"",
|
||||||
@@ -106,8 +65,8 @@ fn showAttr(attr: Attr<Msg>): String =
|
|||||||
OnClick(msg) => match msg {
|
OnClick(msg) => match msg {
|
||||||
Increment => "onclick=\"Increment\"",
|
Increment => "onclick=\"Increment\"",
|
||||||
Decrement => "onclick=\"Decrement\"",
|
Decrement => "onclick=\"Decrement\"",
|
||||||
Reset => "onclick=\"Reset\""
|
Reset => "onclick=\"Reset\"",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showAttrs(attrs: List<Attr<Msg>>): String =
|
fn showAttrs(attrs: List<Attr<Msg>>): String =
|
||||||
@@ -115,8 +74,8 @@ fn showAttrs(attrs: List<Attr<Msg>>): String =
|
|||||||
None => "",
|
None => "",
|
||||||
Some(a) => match List.tail(attrs) {
|
Some(a) => match List.tail(attrs) {
|
||||||
None => showAttr(a),
|
None => showAttr(a),
|
||||||
Some(rest) => showAttr(a) + " " + showAttrs(rest)
|
Some(rest) => showAttr(a) + " " + showAttrs(rest),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showChildren(children: List<Html<Msg>>, indent: Int): String =
|
fn showChildren(children: List<Html<Msg>>, indent: Int): String =
|
||||||
@@ -124,8 +83,8 @@ fn showChildren(children: List<Html<Msg>>, indent: Int): String =
|
|||||||
None => "",
|
None => "",
|
||||||
Some(c) => match List.tail(children) {
|
Some(c) => match List.tail(children) {
|
||||||
None => showHtml(c, indent),
|
None => showHtml(c, indent),
|
||||||
Some(rest) => showHtml(c, indent) + showChildren(rest, indent)
|
Some(rest) => showHtml(c, indent) + showChildren(rest, indent),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showHtml(html: Html<Msg>, indent: Int): String =
|
fn showHtml(html: Html<Msg>, indent: Int): String =
|
||||||
@@ -137,12 +96,8 @@ fn showHtml(html: Html<Msg>, indent: Int): String =
|
|||||||
let attrPart = if String.length(attrStr) > 0 then " " + attrStr else ""
|
let attrPart = if String.length(attrStr) > 0 then " " + attrStr else ""
|
||||||
let childStr = showChildren(children, indent + 2)
|
let childStr = showChildren(children, indent + 2)
|
||||||
"<" + tag + attrPart + ">" + childStr + "</" + tag + ">"
|
"<" + tag + attrPart + ">" + childStr + "</" + tag + ">"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Entry point
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let model = init()
|
let model = init()
|
||||||
@@ -150,24 +105,19 @@ fn main(): Unit with {Console} = {
|
|||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("Initial count: " + toString(getCount(model)))
|
Console.print("Initial count: " + toString(getCount(model)))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
let m1 = update(model, Increment)
|
let m1 = update(model, Increment)
|
||||||
Console.print("After Increment: " + toString(getCount(m1)))
|
Console.print("After Increment: " + toString(getCount(m1)))
|
||||||
|
|
||||||
let m2 = update(m1, Increment)
|
let m2 = update(m1, Increment)
|
||||||
Console.print("After Increment: " + toString(getCount(m2)))
|
Console.print("After Increment: " + toString(getCount(m2)))
|
||||||
|
|
||||||
let m3 = update(m2, Increment)
|
let m3 = update(m2, Increment)
|
||||||
Console.print("After Increment: " + toString(getCount(m3)))
|
Console.print("After Increment: " + toString(getCount(m3)))
|
||||||
|
|
||||||
let m4 = update(m3, Decrement)
|
let m4 = update(m3, Decrement)
|
||||||
Console.print("After Decrement: " + toString(getCount(m4)))
|
Console.print("After Decrement: " + toString(getCount(m4)))
|
||||||
|
|
||||||
let m5 = update(m4, Reset)
|
let m5 = update(m4, Reset)
|
||||||
Console.print("After Reset: " + toString(getCount(m5)))
|
Console.print("After Reset: " + toString(getCount(m5)))
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("=== View (HTML Structure) ===")
|
Console.print("=== View (HTML Structure) ===")
|
||||||
Console.print(showHtml(view(m2), 0))
|
Console.print(showHtml(view(m2), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = run main() with {}
|
let output = run main() with {}
|
||||||
|
|||||||
@@ -1,57 +1,37 @@
|
|||||||
// Demonstrating algebraic data types and pattern matching
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// Tree sum: 8
|
|
||||||
// Tree depth: 3
|
|
||||||
// Safe divide 10/2: Result: 5
|
|
||||||
// Safe divide 10/0: Division by zero!
|
|
||||||
|
|
||||||
// Define a binary tree
|
|
||||||
type Tree =
|
type Tree =
|
||||||
| Leaf(Int)
|
| Leaf(Int)
|
||||||
| Node(Tree, Tree)
|
| Node(Tree, Tree)
|
||||||
|
|
||||||
// Sum all values in a tree
|
|
||||||
fn sumTree(tree: Tree): Int =
|
fn sumTree(tree: Tree): Int =
|
||||||
match tree {
|
match tree {
|
||||||
Leaf(n) => n,
|
Leaf(n) => n,
|
||||||
Node(left, right) => sumTree(left) + sumTree(right)
|
Node(left, right) => sumTree(left) + sumTree(right),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the depth of a tree
|
|
||||||
fn depth(tree: Tree): Int =
|
fn depth(tree: Tree): Int =
|
||||||
match tree {
|
match tree {
|
||||||
Leaf(_) => 1,
|
Leaf(_) => 1,
|
||||||
Node(left, right) => {
|
Node(left, right) => {
|
||||||
let leftDepth = depth(left)
|
let leftDepth = depth(left)
|
||||||
let rightDepth = depth(right)
|
let rightDepth = depth(right)
|
||||||
1 + (if leftDepth > rightDepth then leftDepth else rightDepth)
|
1 + if leftDepth > rightDepth then leftDepth else rightDepth
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Example tree:
|
|
||||||
// Node
|
|
||||||
// / \
|
|
||||||
// Node Leaf(5)
|
|
||||||
// / \
|
|
||||||
// Leaf(1) Leaf(2)
|
|
||||||
|
|
||||||
let myTree = Node(Node(Leaf(1), Leaf(2)), Leaf(5))
|
let myTree = Node(Node(Leaf(1), Leaf(2)), Leaf(5))
|
||||||
|
|
||||||
let treeSum = sumTree(myTree)
|
let treeSum = sumTree(myTree)
|
||||||
|
|
||||||
let treeDepth = depth(myTree)
|
let treeDepth = depth(myTree)
|
||||||
|
|
||||||
// Option type example
|
fn safeDivide(a: Int, b: Int): Option<Int> = if b == 0 then None else Some(a / b)
|
||||||
fn safeDivide(a: Int, b: Int): Option<Int> =
|
|
||||||
if b == 0 then None
|
|
||||||
else Some(a / b)
|
|
||||||
|
|
||||||
fn showResult(result: Option<Int>): String =
|
fn showResult(result: Option<Int>): String =
|
||||||
match result {
|
match result {
|
||||||
None => "Division by zero!",
|
None => "Division by zero!",
|
||||||
Some(n) => "Result: " + toString(n)
|
Some(n) => "Result: " + toString(n),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("Tree sum: " + toString(treeSum))
|
Console.print("Tree sum: " + toString(treeSum))
|
||||||
Console.print("Tree depth: " + toString(treeDepth))
|
Console.print("Tree depth: " + toString(treeDepth))
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
// Demonstrating algebraic effects in Lux
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// [info] Processing data...
|
|
||||||
// [debug] Result computed
|
|
||||||
// Final result: 42
|
|
||||||
|
|
||||||
// Define a custom logging effect
|
|
||||||
effect Logger {
|
effect Logger {
|
||||||
fn log(level: String, msg: String): Unit
|
fn log(level: String, msg: String): Unit
|
||||||
fn getLevel(): String
|
fn getLevel(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
// A function that uses the Logger effect
|
|
||||||
fn processData(data: Int): Int with {Logger} = {
|
fn processData(data: Int): Int with {Logger} = {
|
||||||
Logger.log("info", "Processing data...")
|
Logger.log("info", "Processing data...")
|
||||||
let result = data * 2
|
let result = data * 2
|
||||||
@@ -19,16 +10,14 @@ fn processData(data: Int): Int with {Logger} = {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// A handler that prints logs to console
|
|
||||||
handler consoleLogger: Logger {
|
handler consoleLogger: Logger {
|
||||||
fn log(level, msg) = Console.print("[" + level + "] " + msg)
|
fn log(level, msg) = Console.print("[" + level + "] " + msg)
|
||||||
fn getLevel() = "debug"
|
fn getLevel() = "debug"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run and print
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let result = run processData(21) with {
|
let result = run processData(21) with {
|
||||||
Logger = consoleLogger
|
Logger = consoleLogger,
|
||||||
}
|
}
|
||||||
Console.print("Final result: " + toString(result))
|
Console.print("Final result: " + toString(result))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
// Factorial function demonstrating recursion
|
fn factorial(n: Int): Int = if n <= 1 then 1 else n * factorial(n - 1)
|
||||||
//
|
|
||||||
// Expected output: 10! = 3628800
|
|
||||||
|
|
||||||
fn factorial(n: Int): Int =
|
|
||||||
if n <= 1 then 1
|
|
||||||
else n * factorial(n - 1)
|
|
||||||
|
|
||||||
// Calculate factorial of 10
|
|
||||||
let result = factorial(10)
|
let result = factorial(10)
|
||||||
|
|
||||||
// Print result using Console effect
|
fn showResult(): Unit with {Console} = Console.print("10! = " + toString(result))
|
||||||
fn showResult(): Unit with {Console} =
|
|
||||||
Console.print("10! = " + toString(result))
|
|
||||||
|
|
||||||
let output = run showResult() with {}
|
let output = run showResult() with {}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
// File I/O example - demonstrates the File effect
|
|
||||||
//
|
|
||||||
// This script reads a file, counts lines/words, and writes a report
|
|
||||||
|
|
||||||
fn countLines(content: String): Int = {
|
fn countLines(content: String): Int = {
|
||||||
let lines = String.split(content, "\n")
|
let lines = String.split(content, "
|
||||||
|
")
|
||||||
List.length(lines)
|
List.length(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,13 +11,11 @@ fn countWords(content: String): Int = {
|
|||||||
|
|
||||||
fn analyzeFile(path: String): Unit with {File, Console} = {
|
fn analyzeFile(path: String): Unit with {File, Console} = {
|
||||||
Console.print("Analyzing file: " + path)
|
Console.print("Analyzing file: " + path)
|
||||||
|
|
||||||
if File.exists(path) then {
|
if File.exists(path) then {
|
||||||
let content = File.read(path)
|
let content = File.read(path)
|
||||||
let lines = countLines(content)
|
let lines = countLines(content)
|
||||||
let words = countWords(content)
|
let words = countWords(content)
|
||||||
let chars = String.length(content)
|
let chars = String.length(content)
|
||||||
|
|
||||||
Console.print(" Lines: " + toString(lines))
|
Console.print(" Lines: " + toString(lines))
|
||||||
Console.print(" Words: " + toString(words))
|
Console.print(" Words: " + toString(words))
|
||||||
Console.print(" Chars: " + toString(chars))
|
Console.print(" Chars: " + toString(chars))
|
||||||
@@ -32,17 +27,12 @@ fn analyzeFile(path: String): Unit with {File, Console} = {
|
|||||||
fn main(): Unit with {File, Console} = {
|
fn main(): Unit with {File, Console} = {
|
||||||
Console.print("=== Lux File Analyzer ===")
|
Console.print("=== Lux File Analyzer ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Analyze this file itself
|
|
||||||
analyzeFile("examples/file_io.lux")
|
analyzeFile("examples/file_io.lux")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Analyze hello.lux
|
|
||||||
analyzeFile("examples/hello.lux")
|
analyzeFile("examples/hello.lux")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
let report = "File analysis complete.
|
||||||
// Write a report
|
Analyzed 2 files."
|
||||||
let report = "File analysis complete.\nAnalyzed 2 files."
|
|
||||||
File.write("/tmp/lux_report.txt", report)
|
File.write("/tmp/lux_report.txt", report)
|
||||||
Console.print("Report written to /tmp/lux_report.txt")
|
Console.print("Report written to /tmp/lux_report.txt")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,39 @@
|
|||||||
// Demonstrating functional programming features
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// apply(double, 21) = 42
|
|
||||||
// compose(addOne, double)(5) = 11
|
|
||||||
// pipe: 5 |> double |> addOne |> square = 121
|
|
||||||
// curried add5(10) = 15
|
|
||||||
// partial times3(7) = 21
|
|
||||||
// record transform = 30
|
|
||||||
|
|
||||||
// Higher-order functions
|
|
||||||
fn apply(f: fn(Int): Int, x: Int): Int = f(x)
|
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 compose(f: fn(Int): Int, g: fn(Int): Int): fn(Int): Int = fn(x: Int): Int => f(g(x))
|
||||||
fn(x: Int): Int => f(g(x))
|
|
||||||
|
|
||||||
// Basic functions
|
|
||||||
fn double(x: Int): Int = x * 2
|
fn double(x: Int): Int = x * 2
|
||||||
|
|
||||||
fn addOne(x: Int): Int = x + 1
|
fn addOne(x: Int): Int = x + 1
|
||||||
|
|
||||||
fn square(x: Int): Int = x * x
|
fn square(x: Int): Int = x * x
|
||||||
|
|
||||||
// Using apply
|
|
||||||
let result1 = apply(double, 21)
|
let result1 = apply(double, 21)
|
||||||
|
|
||||||
// Using compose
|
|
||||||
let doubleAndAddOne = compose(addOne, double)
|
let doubleAndAddOne = compose(addOne, double)
|
||||||
|
|
||||||
let result2 = doubleAndAddOne(5)
|
let result2 = doubleAndAddOne(5)
|
||||||
|
|
||||||
// Using the pipe operator
|
let result3 = square(addOne(double(5)))
|
||||||
let result3 = 5 |> double |> addOne |> square
|
|
||||||
|
|
||||||
// Currying example
|
fn add(a: Int): fn(Int): Int = fn(b: Int): Int => a + b
|
||||||
fn add(a: Int): fn(Int): Int =
|
|
||||||
fn(b: Int): Int => a + b
|
|
||||||
|
|
||||||
let add5 = add(5)
|
let add5 = add(5)
|
||||||
|
|
||||||
let result4 = add5(10)
|
let result4 = add5(10)
|
||||||
|
|
||||||
// Partial application simulation
|
|
||||||
fn multiply(a: Int, b: Int): Int = a * b
|
fn multiply(a: Int, b: Int): Int = a * b
|
||||||
|
|
||||||
let times3 = fn(x: Int): Int => multiply(3, x)
|
let times3 = fn(x: Int): Int => multiply(3, x)
|
||||||
|
|
||||||
let result5 = times3(7)
|
let result5 = times3(7)
|
||||||
|
|
||||||
// Working with records
|
let transform = fn(record: { x: Int, y: Int }): Int => record.x + record.y
|
||||||
let transform = fn(record: { x: Int, y: Int }): Int =>
|
|
||||||
record.x + record.y
|
|
||||||
|
|
||||||
let point = { x: 10, y: 20 }
|
let point = { x: 10, y: 20 }
|
||||||
|
|
||||||
let recordSum = transform(point)
|
let recordSum = transform(point)
|
||||||
|
|
||||||
// Print all results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("apply(double, 21) = " + toString(result1))
|
Console.print("apply(double, 21) = " + toString(result1))
|
||||||
Console.print("compose(addOne, double)(5) = " + toString(result2))
|
Console.print("compose(addOne, double)(5) = " + toString(result2))
|
||||||
|
|||||||
@@ -1,45 +1,34 @@
|
|||||||
// Demonstrating generic type parameters in Lux
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// identity(42) = 42
|
|
||||||
// identity("hello") = hello
|
|
||||||
// first(MkPair(1, "one")) = 1
|
|
||||||
// second(MkPair(1, "one")) = one
|
|
||||||
// map(Some(21), double) = Some(42)
|
|
||||||
|
|
||||||
// Generic identity function
|
|
||||||
fn identity<T>(x: T): T = x
|
fn identity<T>(x: T): T = x
|
||||||
|
|
||||||
// Generic pair type
|
|
||||||
type Pair<A, B> =
|
type Pair<A, B> =
|
||||||
| MkPair(A, B)
|
| MkPair(A, B)
|
||||||
|
|
||||||
fn first<A, B>(p: Pair<A, B>): A =
|
fn first<A, B>(p: Pair<A, B>): A =
|
||||||
match p {
|
match p {
|
||||||
MkPair(a, _) => a
|
MkPair(a, _) => a,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn second<A, B>(p: Pair<A, B>): B =
|
fn second<A, B>(p: Pair<A, B>): B =
|
||||||
match p {
|
match p {
|
||||||
MkPair(_, b) => b
|
MkPair(_, b) => b,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic map function for Option
|
|
||||||
fn mapOption<T, U>(opt: Option<T>, f: fn(T): U): Option<U> =
|
fn mapOption<T, U>(opt: Option<T>, f: fn(T): U): Option<U> =
|
||||||
match opt {
|
match opt {
|
||||||
None => None,
|
None => None,
|
||||||
Some(x) => Some(f(x))
|
Some(x) => Some(f(x)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for testing
|
|
||||||
fn double(x: Int): Int = x * 2
|
fn double(x: Int): Int = x * 2
|
||||||
|
|
||||||
// Test usage
|
|
||||||
let id_int = identity(42)
|
let id_int = identity(42)
|
||||||
|
|
||||||
let id_str = identity("hello")
|
let id_str = identity("hello")
|
||||||
|
|
||||||
let pair = MkPair(1, "one")
|
let pair = MkPair(1, "one")
|
||||||
|
|
||||||
let fst = first(pair)
|
let fst = first(pair)
|
||||||
|
|
||||||
let snd = second(pair)
|
let snd = second(pair)
|
||||||
|
|
||||||
let doubled = mapOption(Some(21), double)
|
let doubled = mapOption(Some(21), double)
|
||||||
@@ -47,7 +36,7 @@ let doubled = mapOption(Some(21), double)
|
|||||||
fn showOption(opt: Option<Int>): String =
|
fn showOption(opt: Option<Int>): String =
|
||||||
match opt {
|
match opt {
|
||||||
None => "None",
|
None => "None",
|
||||||
Some(x) => "Some(" + toString(x) + ")"
|
Some(x) => "Some(" + toString(x) + ")",
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
|
|||||||
@@ -1,21 +1,8 @@
|
|||||||
// Demonstrating resumable effect handlers in Lux
|
|
||||||
//
|
|
||||||
// Handlers can use `resume(value)` to return a value to the effect call site
|
|
||||||
// and continue the computation. This enables powerful control flow patterns.
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// [INFO] Starting computation
|
|
||||||
// [DEBUG] Intermediate result: 10
|
|
||||||
// [INFO] Computation complete
|
|
||||||
// Final result: 20
|
|
||||||
|
|
||||||
// Define a custom logging effect
|
|
||||||
effect Logger {
|
effect Logger {
|
||||||
fn log(level: String, msg: String): Unit
|
fn log(level: String, msg: String): Unit
|
||||||
fn getLogLevel(): String
|
fn getLogLevel(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
// A function that uses the Logger effect
|
|
||||||
fn compute(): Int with {Logger} = {
|
fn compute(): Int with {Logger} = {
|
||||||
Logger.log("INFO", "Starting computation")
|
Logger.log("INFO", "Starting computation")
|
||||||
let x = 10
|
let x = 10
|
||||||
@@ -25,19 +12,18 @@ fn compute(): Int with {Logger} = {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// A handler that prints logs with brackets and resumes with Unit
|
|
||||||
handler prettyLogger: Logger {
|
handler prettyLogger: Logger {
|
||||||
fn log(level, msg) = {
|
fn log(level, msg) =
|
||||||
|
{
|
||||||
Console.print("[" + level + "] " + msg)
|
Console.print("[" + level + "] " + msg)
|
||||||
resume(())
|
resume(())
|
||||||
}
|
}
|
||||||
fn getLogLevel() = resume("DEBUG")
|
fn getLogLevel() = resume("DEBUG")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main function
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let result = run compute() with {
|
let result = run compute() with {
|
||||||
Logger = prettyLogger
|
Logger = prettyLogger,
|
||||||
}
|
}
|
||||||
Console.print("Final result: " + toString(result))
|
Console.print("Final result: " + toString(result))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
// Hello World in Lux
|
fn greet(): Unit with {Console} = Console.print("Hello, World!")
|
||||||
// Demonstrates basic effect usage
|
|
||||||
//
|
|
||||||
// Expected output: Hello, World!
|
|
||||||
|
|
||||||
fn greet(): Unit with {Console} =
|
|
||||||
Console.print("Hello, World!")
|
|
||||||
|
|
||||||
// Run the greeting with the Console effect
|
|
||||||
let output = run greet() with {}
|
let output = run greet() with {}
|
||||||
|
|||||||
@@ -1,90 +1,71 @@
|
|||||||
// HTTP example - demonstrates the Http effect
|
|
||||||
//
|
|
||||||
// This script makes HTTP requests and parses JSON responses
|
|
||||||
|
|
||||||
fn main(): Unit with {Console, Http} = {
|
fn main(): Unit with {Console, Http} = {
|
||||||
Console.print("=== Lux HTTP Example ===")
|
Console.print("=== Lux HTTP Example ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Make a GET request to a public API
|
|
||||||
Console.print("Fetching data from httpbin.org...")
|
Console.print("Fetching data from httpbin.org...")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
match Http.get("https://httpbin.org/get") {
|
match Http.get("https://httpbin.org/get") {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
Console.print("GET request successful!")
|
Console.print("GET request successful!")
|
||||||
Console.print(" Status: " + toString(response.status))
|
Console.print(" Status: " + toString(response.status))
|
||||||
Console.print(" Body length: " + toString(String.length(response.body)) + " bytes")
|
Console.print(" Body length: " + toString(String.length(response.body)) + " bytes")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Parse the JSON response
|
|
||||||
match Json.parse(response.body) {
|
match Json.parse(response.body) {
|
||||||
Ok(json) => {
|
Ok(json) => {
|
||||||
Console.print("Parsed JSON response:")
|
Console.print("Parsed JSON response:")
|
||||||
match Json.get(json, "origin") {
|
match Json.get(json, "origin") {
|
||||||
Some(origin) => match Json.asString(origin) {
|
Some(origin) => match Json.asString(origin) {
|
||||||
Some(ip) => Console.print(" Your IP: " + ip),
|
Some(ip) => Console.print(" Your IP: " + ip),
|
||||||
None => Console.print(" origin: (not a string)")
|
None => Console.print(" origin: (not a string)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" origin: (not found)")
|
None => Console.print(" origin: (not found)"),
|
||||||
}
|
}
|
||||||
match Json.get(json, "url") {
|
match Json.get(json, "url") {
|
||||||
Some(url) => match Json.asString(url) {
|
Some(url) => match Json.asString(url) {
|
||||||
Some(u) => Console.print(" URL: " + u),
|
Some(u) => Console.print(" URL: " + u),
|
||||||
None => Console.print(" url: (not a string)")
|
None => Console.print(" url: (not a string)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" url: (not found)")
|
None => Console.print(" url: (not found)"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => Console.print("JSON parse error: " + e)
|
Err(e) => Console.print("JSON parse error: " + e),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => Console.print("GET request failed: " + e)
|
Err(e) => Console.print("GET request failed: " + e),
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("--- POST Request ---")
|
Console.print("--- POST Request ---")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Make a POST request with JSON body
|
|
||||||
let requestBody = Json.object([("message", Json.string("Hello from Lux!")), ("version", Json.int(1))])
|
let requestBody = Json.object([("message", Json.string("Hello from Lux!")), ("version", Json.int(1))])
|
||||||
Console.print("Sending POST with JSON body...")
|
Console.print("Sending POST with JSON body...")
|
||||||
Console.print(" Body: " + Json.stringify(requestBody))
|
Console.print(" Body: " + Json.stringify(requestBody))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
match Http.postJson("https://httpbin.org/post", requestBody) {
|
match Http.postJson("https://httpbin.org/post", requestBody) {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
Console.print("POST request successful!")
|
Console.print("POST request successful!")
|
||||||
Console.print(" Status: " + toString(response.status))
|
Console.print(" Status: " + toString(response.status))
|
||||||
|
|
||||||
// Parse and extract what we sent
|
|
||||||
match Json.parse(response.body) {
|
match Json.parse(response.body) {
|
||||||
Ok(json) => match Json.get(json, "json") {
|
Ok(json) => match Json.get(json, "json") {
|
||||||
Some(sentJson) => {
|
Some(sentJson) => {
|
||||||
Console.print(" Server received:")
|
Console.print(" Server received:")
|
||||||
Console.print(" " + Json.stringify(sentJson))
|
Console.print(" " + Json.stringify(sentJson))
|
||||||
},
|
},
|
||||||
None => Console.print(" (no json field in response)")
|
None => Console.print(" (no json field in response)"),
|
||||||
},
|
},
|
||||||
Err(e) => Console.print("JSON parse error: " + e)
|
Err(e) => Console.print("JSON parse error: " + e),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => Console.print("POST request failed: " + e)
|
Err(e) => Console.print("POST request failed: " + e),
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("--- Headers ---")
|
Console.print("--- Headers ---")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Show response headers
|
|
||||||
match Http.get("https://httpbin.org/headers") {
|
match Http.get("https://httpbin.org/headers") {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
Console.print("Response headers (first 5):")
|
Console.print("Response headers (first 5):")
|
||||||
let count = 0
|
let count = 0
|
||||||
// Note: Can't easily iterate with effects in callbacks, so just show count
|
|
||||||
Console.print(" Total headers: " + toString(List.length(response.headers)))
|
Console.print(" Total headers: " + toString(List.length(response.headers)))
|
||||||
},
|
},
|
||||||
Err(e) => Console.print("Request failed: " + e)
|
Err(e) => Console.print("Request failed: " + e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +1,31 @@
|
|||||||
// HTTP API Example
|
fn httpOk(body: String): { status: Int, body: String } = { status: 200, body: body }
|
||||||
//
|
|
||||||
// A complete REST API demonstrating:
|
|
||||||
// - Route matching with path parameters
|
|
||||||
// - Response builders
|
|
||||||
// - JSON construction
|
|
||||||
//
|
|
||||||
// Run with: lux examples/http_api.lux
|
|
||||||
// Test with:
|
|
||||||
// curl http://localhost:8080/
|
|
||||||
// curl http://localhost:8080/users
|
|
||||||
// curl http://localhost:8080/users/42
|
|
||||||
|
|
||||||
// ============================================================
|
fn httpCreated(body: String): { status: Int, body: String } = { status: 201, body: body }
|
||||||
// Response Helpers
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn httpOk(body: String): { status: Int, body: String } =
|
fn httpNotFound(body: String): { status: Int, body: String } = { status: 404, body: body }
|
||||||
{ status: 200, body: body }
|
|
||||||
|
|
||||||
fn httpCreated(body: String): { status: Int, body: String } =
|
fn httpBadRequest(body: String): { status: Int, body: String } = { status: 400, body: body }
|
||||||
{ status: 201, body: body }
|
|
||||||
|
|
||||||
fn httpNotFound(body: String): { status: Int, body: String } =
|
fn jsonEscape(s: String): String = String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"")
|
||||||
{ status: 404, body: body }
|
|
||||||
|
|
||||||
fn httpBadRequest(body: String): { status: Int, body: String } =
|
fn jsonStr(key: String, value: String): String = "\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\""
|
||||||
{ status: 400, body: body }
|
|
||||||
|
|
||||||
// ============================================================
|
fn jsonNum(key: String, value: Int): String = "\"" + jsonEscape(key) + "\":" + toString(value)
|
||||||
// JSON Helpers
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn jsonEscape(s: String): String =
|
fn jsonObj(content: String): String = toString(" + content + ")
|
||||||
String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"")
|
|
||||||
|
|
||||||
fn jsonStr(key: String, value: String): String =
|
fn jsonArr(content: String): String = "[" + content + "]"
|
||||||
"\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\""
|
|
||||||
|
|
||||||
fn jsonNum(key: String, value: Int): String =
|
fn jsonError(message: String): String = jsonObj(jsonStr("error", message))
|
||||||
"\"" + jsonEscape(key) + "\":" + toString(value)
|
|
||||||
|
|
||||||
fn jsonObj(content: String): String =
|
|
||||||
"{" + content + "}"
|
|
||||||
|
|
||||||
fn jsonArr(content: String): String =
|
|
||||||
"[" + content + "]"
|
|
||||||
|
|
||||||
fn jsonError(message: String): String =
|
|
||||||
jsonObj(jsonStr("error", message))
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Path Matching
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn pathMatches(path: String, pattern: String): Bool = {
|
fn pathMatches(path: String, pattern: String): Bool = {
|
||||||
let pathParts = String.split(path, "/")
|
let pathParts = String.split(path, "/")
|
||||||
let patternParts = String.split(pattern, "/")
|
let patternParts = String.split(pattern, "/")
|
||||||
if List.length(pathParts) != List.length(patternParts) then false
|
if List.length(pathParts) != List.length(patternParts) then false else matchParts(pathParts, patternParts)
|
||||||
else matchParts(pathParts, patternParts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchParts(pathParts: List<String>, patternParts: List<String>): Bool = {
|
fn matchParts(pathParts: List<String>, patternParts: List<String>): Bool = {
|
||||||
if List.length(pathParts) == 0 then true
|
if List.length(pathParts) == 0 then true else {
|
||||||
else {
|
|
||||||
match List.head(pathParts) {
|
match List.head(pathParts) {
|
||||||
None => true,
|
None => true,
|
||||||
Some(pathPart) => {
|
Some(pathPart) => {
|
||||||
@@ -75,9 +38,9 @@ fn matchParts(pathParts: List<String>, patternParts: List<String>): Bool = {
|
|||||||
let restPattern = Option.getOrElse(List.tail(patternParts), [])
|
let restPattern = Option.getOrElse(List.tail(patternParts), [])
|
||||||
matchParts(restPath, restPattern)
|
matchParts(restPath, restPattern)
|
||||||
} else false
|
} else false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,15 +50,9 @@ fn getPathSegment(path: String, index: Int): Option<String> = {
|
|||||||
List.get(parts, index + 1)
|
List.get(parts, index + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
fn indexHandler(): { status: Int, body: String } = httpOk(jsonObj(jsonStr("message", "Welcome to Lux HTTP API")))
|
||||||
// Handlers
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn indexHandler(): { status: Int, body: String } =
|
fn healthHandler(): { status: Int, body: String } = httpOk(jsonObj(jsonStr("status", "healthy")))
|
||||||
httpOk(jsonObj(jsonStr("message", "Welcome to Lux HTTP API")))
|
|
||||||
|
|
||||||
fn healthHandler(): { status: Int, body: String } =
|
|
||||||
httpOk(jsonObj(jsonStr("status", "healthy")))
|
|
||||||
|
|
||||||
fn listUsersHandler(): { status: Int, body: String } = {
|
fn listUsersHandler(): { status: Int, body: String } = {
|
||||||
let user1 = jsonObj(jsonNum("id", 1) + "," + jsonStr("name", "Alice"))
|
let user1 = jsonObj(jsonNum("id", 1) + "," + jsonStr("name", "Alice"))
|
||||||
@@ -109,7 +66,7 @@ fn getUserHandler(path: String): { status: Int, body: String } = {
|
|||||||
let body = jsonObj(jsonStr("id", id) + "," + jsonStr("name", "User " + id))
|
let body = jsonObj(jsonStr("id", id) + "," + jsonStr("name", "User " + id))
|
||||||
httpOk(body)
|
httpOk(body)
|
||||||
},
|
},
|
||||||
None => httpNotFound(jsonError("User not found"))
|
None => httpNotFound(jsonError("User not found")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,23 +75,10 @@ fn createUserHandler(body: String): { status: Int, body: String } = {
|
|||||||
httpCreated(newUser)
|
httpCreated(newUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Router
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn router(method: String, path: String, body: String): { status: Int, body: String } = {
|
fn router(method: String, path: String, body: String): { status: Int, body: String } = {
|
||||||
if method == "GET" && path == "/" then indexHandler()
|
if method == "GET" && path == "/" then indexHandler() else if method == "GET" && path == "/health" then healthHandler() else if method == "GET" && path == "/users" then listUsersHandler() else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path) else if method == "POST" && path == "/users" then createUserHandler(body) else httpNotFound(jsonError("Not found: " + path))
|
||||||
else if method == "GET" && path == "/health" then healthHandler()
|
|
||||||
else if method == "GET" && path == "/users" then listUsersHandler()
|
|
||||||
else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path)
|
|
||||||
else if method == "POST" && path == "/users" then createUserHandler(body)
|
|
||||||
else httpNotFound(jsonError("Not found: " + path))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Server
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = {
|
fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = {
|
||||||
if remaining <= 0 then {
|
if remaining <= 0 then {
|
||||||
Console.print("Max requests reached, stopping server.")
|
Console.print("Max requests reached, stopping server.")
|
||||||
|
|||||||
@@ -1,24 +1,4 @@
|
|||||||
// HTTP Router Example
|
fn indexHandler(): { status: Int, body: String } = httpOk("Welcome to Lux HTTP Framework!")
|
||||||
//
|
|
||||||
// Demonstrates the HTTP helper library with:
|
|
||||||
// - Path pattern matching
|
|
||||||
// - Response builders
|
|
||||||
// - JSON helpers
|
|
||||||
//
|
|
||||||
// Run with: lux examples/http_router.lux
|
|
||||||
// Test with:
|
|
||||||
// curl http://localhost:8080/
|
|
||||||
// curl http://localhost:8080/users
|
|
||||||
// curl http://localhost:8080/users/42
|
|
||||||
|
|
||||||
import stdlib/http
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Route Handlers
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn indexHandler(): { status: Int, body: String } =
|
|
||||||
httpOk("Welcome to Lux HTTP Framework!")
|
|
||||||
|
|
||||||
fn listUsersHandler(): { status: Int, body: String } = {
|
fn listUsersHandler(): { status: Int, body: String } = {
|
||||||
let user1 = jsonObject(jsonJoin([jsonNumber("id", 1), jsonString("name", "Alice")]))
|
let user1 = jsonObject(jsonJoin([jsonNumber("id", 1), jsonString("name", "Alice")]))
|
||||||
@@ -33,29 +13,16 @@ fn getUserHandler(path: String): { status: Int, body: String } = {
|
|||||||
let body = jsonObject(jsonJoin([jsonString("id", id), jsonString("name", "User " + id)]))
|
let body = jsonObject(jsonJoin([jsonString("id", id), jsonString("name", "User " + id)]))
|
||||||
httpOk(body)
|
httpOk(body)
|
||||||
},
|
},
|
||||||
None => httpNotFound(jsonErrorMsg("User ID required"))
|
None => httpNotFound(jsonErrorMsg("User ID required")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn healthHandler(): { status: Int, body: String } =
|
fn healthHandler(): { status: Int, body: String } = httpOk(jsonObject(jsonString("status", "healthy")))
|
||||||
httpOk(jsonObject(jsonString("status", "healthy")))
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Router
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn router(method: String, path: String, body: String): { status: Int, body: String } = {
|
fn router(method: String, path: String, body: String): { status: Int, body: String } = {
|
||||||
if method == "GET" && path == "/" then indexHandler()
|
if method == "GET" && path == "/" then indexHandler() else if method == "GET" && path == "/health" then healthHandler() else if method == "GET" && path == "/users" then listUsersHandler() else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path) else httpNotFound(jsonErrorMsg("Not found: " + path))
|
||||||
else if method == "GET" && path == "/health" then healthHandler()
|
|
||||||
else if method == "GET" && path == "/users" then listUsersHandler()
|
|
||||||
else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path)
|
|
||||||
else httpNotFound(jsonErrorMsg("Not found: " + path))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Server
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = {
|
fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = {
|
||||||
if remaining <= 0 then {
|
if remaining <= 0 then {
|
||||||
Console.print("Max requests reached, stopping server.")
|
Console.print("Max requests reached, stopping server.")
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
// Test file for JIT compilation
|
fn fib(n: Int): Int = if n <= 1 then n else fib(n - 1) + fib(n - 2)
|
||||||
// This uses only features the JIT supports: integers, arithmetic, conditionals, functions
|
|
||||||
|
|
||||||
fn fib(n: Int): Int =
|
fn factorial(n: Int): Int = if n <= 1 then 1 else n * factorial(n - 1)
|
||||||
if n <= 1 then n
|
|
||||||
else fib(n - 1) + fib(n - 2)
|
|
||||||
|
|
||||||
fn factorial(n: Int): Int =
|
|
||||||
if n <= 1 then 1
|
|
||||||
else n * factorial(n - 1)
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let fibResult = fib(30)
|
let fibResult = fib(30)
|
||||||
|
|||||||
@@ -1,107 +1,79 @@
|
|||||||
// JSON example - demonstrates JSON parsing and manipulation
|
|
||||||
//
|
|
||||||
// This script parses JSON, extracts values, and builds new JSON structures
|
|
||||||
|
|
||||||
fn main(): Unit with {Console, File} = {
|
fn main(): Unit with {Console, File} = {
|
||||||
Console.print("=== Lux JSON Example ===")
|
Console.print("=== Lux JSON Example ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// First, build some JSON programmatically
|
|
||||||
Console.print("=== Building JSON ===")
|
Console.print("=== Building JSON ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
let name = Json.string("Alice")
|
let name = Json.string("Alice")
|
||||||
let age = Json.int(30)
|
let age = Json.int(30)
|
||||||
let active = Json.bool(true)
|
let active = Json.bool(true)
|
||||||
let scores = Json.array([Json.int(95), Json.int(87), Json.int(92)])
|
let scores = Json.array([Json.int(95), Json.int(87), Json.int(92)])
|
||||||
|
|
||||||
let person = Json.object([("name", name), ("age", age), ("active", active), ("scores", scores)])
|
let person = Json.object([("name", name), ("age", age), ("active", active), ("scores", scores)])
|
||||||
|
|
||||||
Console.print("Built JSON:")
|
Console.print("Built JSON:")
|
||||||
let pretty = Json.prettyPrint(person)
|
let pretty = Json.prettyPrint(person)
|
||||||
Console.print(pretty)
|
Console.print(pretty)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Stringify to a compact string
|
|
||||||
let jsonStr = Json.stringify(person)
|
let jsonStr = Json.stringify(person)
|
||||||
Console.print("Compact: " + jsonStr)
|
Console.print("Compact: " + jsonStr)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Write to file and read back to test parsing
|
|
||||||
File.write("/tmp/test.json", jsonStr)
|
File.write("/tmp/test.json", jsonStr)
|
||||||
Console.print("Written to /tmp/test.json")
|
Console.print("Written to /tmp/test.json")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Read and parse from file
|
|
||||||
Console.print("=== Parsing JSON ===")
|
Console.print("=== Parsing JSON ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
let content = File.read("/tmp/test.json")
|
let content = File.read("/tmp/test.json")
|
||||||
Console.print("Read from file: " + content)
|
Console.print("Read from file: " + content)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
match Json.parse(content) {
|
match Json.parse(content) {
|
||||||
Ok(json) => {
|
Ok(json) => {
|
||||||
Console.print("Parse succeeded!")
|
Console.print("Parse succeeded!")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Get string field
|
|
||||||
Console.print("Extracting fields:")
|
Console.print("Extracting fields:")
|
||||||
match Json.get(json, "name") {
|
match Json.get(json, "name") {
|
||||||
Some(nameJson) => match Json.asString(nameJson) {
|
Some(nameJson) => match Json.asString(nameJson) {
|
||||||
Some(n) => Console.print(" name: " + n),
|
Some(n) => Console.print(" name: " + n),
|
||||||
None => Console.print(" name: (not a string)")
|
None => Console.print(" name: (not a string)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" name: (not found)")
|
None => Console.print(" name: (not found)"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get int field
|
|
||||||
match Json.get(json, "age") {
|
match Json.get(json, "age") {
|
||||||
Some(ageJson) => match Json.asInt(ageJson) {
|
Some(ageJson) => match Json.asInt(ageJson) {
|
||||||
Some(a) => Console.print(" age: " + toString(a)),
|
Some(a) => Console.print(" age: " + toString(a)),
|
||||||
None => Console.print(" age: (not an int)")
|
None => Console.print(" age: (not an int)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" age: (not found)")
|
None => Console.print(" age: (not found)"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get bool field
|
|
||||||
match Json.get(json, "active") {
|
match Json.get(json, "active") {
|
||||||
Some(activeJson) => match Json.asBool(activeJson) {
|
Some(activeJson) => match Json.asBool(activeJson) {
|
||||||
Some(a) => Console.print(" active: " + toString(a)),
|
Some(a) => Console.print(" active: " + toString(a)),
|
||||||
None => Console.print(" active: (not a bool)")
|
None => Console.print(" active: (not a bool)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" active: (not found)")
|
None => Console.print(" active: (not found)"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get array field
|
|
||||||
match Json.get(json, "scores") {
|
match Json.get(json, "scores") {
|
||||||
Some(scoresJson) => match Json.asArray(scoresJson) {
|
Some(scoresJson) => match Json.asArray(scoresJson) {
|
||||||
Some(arr) => {
|
Some(arr) => {
|
||||||
Console.print(" scores: " + toString(List.length(arr)) + " items")
|
Console.print(" scores: " + toString(List.length(arr)) + " items")
|
||||||
// Get first score
|
|
||||||
match Json.getIndex(scoresJson, 0) {
|
match Json.getIndex(scoresJson, 0) {
|
||||||
Some(firstJson) => match Json.asInt(firstJson) {
|
Some(firstJson) => match Json.asInt(firstJson) {
|
||||||
Some(first) => Console.print(" first score: " + toString(first)),
|
Some(first) => Console.print(" first score: " + toString(first)),
|
||||||
None => Console.print(" first score: (not an int)")
|
None => Console.print(" first score: (not an int)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" (no first element)")
|
None => Console.print(" (no first element)"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => Console.print(" scores: (not an array)")
|
None => Console.print(" scores: (not an array)"),
|
||||||
},
|
},
|
||||||
None => Console.print(" scores: (not found)")
|
None => Console.print(" scores: (not found)"),
|
||||||
}
|
}
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Get the keys
|
|
||||||
Console.print("Object keys:")
|
Console.print("Object keys:")
|
||||||
match Json.keys(json) {
|
match Json.keys(json) {
|
||||||
Some(ks) => Console.print(" " + String.join(ks, ", ")),
|
Some(ks) => Console.print(" " + String.join(ks, ", ")),
|
||||||
None => Console.print(" (not an object)")
|
None => Console.print(" (not an object)"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => Console.print("Parse error: " + e)
|
Err(e) => Console.print("Parse error: " + e),
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("=== JSON Null Check ===")
|
Console.print("=== JSON Null Check ===")
|
||||||
let nullVal = Json.null()
|
let nullVal = Json.null()
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
// Main program that imports modules
|
|
||||||
import examples/modules/math_utils
|
|
||||||
import examples/modules/string_utils
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== Testing Module Imports ===")
|
Console.print("=== Testing Module Imports ===")
|
||||||
|
|
||||||
// Use math_utils
|
|
||||||
Console.print("square(5) = " + toString(math_utils.square(5)))
|
Console.print("square(5) = " + toString(math_utils.square(5)))
|
||||||
Console.print("cube(3) = " + toString(math_utils.cube(3)))
|
Console.print("cube(3) = " + toString(math_utils.cube(3)))
|
||||||
Console.print("factorial(6) = " + toString(math_utils.factorial(6)))
|
Console.print("factorial(6) = " + toString(math_utils.factorial(6)))
|
||||||
Console.print("sumRange(1, 10) = " + toString(math_utils.sumRange(1, 10)))
|
Console.print("sumRange(1, 10) = " + toString(math_utils.sumRange(1, 10)))
|
||||||
|
|
||||||
// Use string_utils
|
|
||||||
Console.print(string_utils.greet("World"))
|
Console.print(string_utils.greet("World"))
|
||||||
Console.print(string_utils.exclaim("Modules work"))
|
Console.print(string_utils.exclaim("Modules work"))
|
||||||
Console.print("repeat(\"ab\", 3) = " + string_utils.repeat("ab", 3))
|
Console.print("repeat(\"ab\", 3) = " + string_utils.repeat("ab", 3))
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
// Test selective imports
|
|
||||||
import examples/modules/math_utils.{square, factorial}
|
|
||||||
import examples/modules/string_utils as str
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== Selective & Aliased Imports ===")
|
Console.print("=== Selective & Aliased Imports ===")
|
||||||
|
|
||||||
// Direct imports (no module prefix)
|
|
||||||
Console.print("square(7) = " + toString(square(7)))
|
Console.print("square(7) = " + toString(square(7)))
|
||||||
Console.print("factorial(5) = " + toString(factorial(5)))
|
Console.print("factorial(5) = " + toString(factorial(5)))
|
||||||
|
|
||||||
// Aliased import
|
|
||||||
Console.print(str.greet("Lux"))
|
Console.print(str.greet("Lux"))
|
||||||
Console.print(str.exclaim("Aliased imports work"))
|
Console.print(str.exclaim("Aliased imports work"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
// Test wildcard imports
|
|
||||||
import examples/modules/math_utils.*
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== Wildcard Imports ===")
|
Console.print("=== Wildcard Imports ===")
|
||||||
|
|
||||||
// All functions available directly
|
|
||||||
Console.print("square(4) = " + toString(square(4)))
|
Console.print("square(4) = " + toString(square(4)))
|
||||||
Console.print("cube(4) = " + toString(cube(4)))
|
Console.print("cube(4) = " + toString(cube(4)))
|
||||||
Console.print("factorial(4) = " + toString(factorial(4)))
|
Console.print("factorial(4) = " + toString(factorial(4)))
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
// Math utilities module
|
fn square(n: Int): Int = n * n
|
||||||
// Exports: square, cube, factorial
|
|
||||||
|
|
||||||
pub fn square(n: Int): Int = n * n
|
fn cube(n: Int): Int = n * n * n
|
||||||
|
|
||||||
pub fn cube(n: Int): Int = n * n * n
|
fn factorial(n: Int): Int = if n <= 1 then 1 else n * factorial(n - 1)
|
||||||
|
|
||||||
pub fn factorial(n: Int): Int =
|
fn sumRange(start: Int, end: Int): Int = if start > end then 0 else start + sumRange(start + 1, end)
|
||||||
if n <= 1 then 1
|
|
||||||
else n * factorial(n - 1)
|
|
||||||
|
|
||||||
pub fn sumRange(start: Int, end: Int): Int =
|
|
||||||
if start > end then 0
|
|
||||||
else start + sumRange(start + 1, end)
|
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
// String utilities module
|
fn repeat(s: String, n: Int): String = if n <= 0 then "" else s + repeat(s, n - 1)
|
||||||
// Exports: repeat, exclaim
|
|
||||||
|
|
||||||
pub fn repeat(s: String, n: Int): String =
|
fn exclaim(s: String): String = s + "!"
|
||||||
if n <= 0 then ""
|
|
||||||
else s + repeat(s, n - 1)
|
|
||||||
|
|
||||||
pub fn exclaim(s: String): String = s + "!"
|
fn greet(name: String): String = "Hello, " + name + "!"
|
||||||
|
|
||||||
pub fn greet(name: String): String =
|
|
||||||
"Hello, " + name + "!"
|
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
// Example using the standard library
|
|
||||||
import std/prelude.*
|
|
||||||
import std/option as opt
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== Using Standard Library ===")
|
Console.print("=== Using Standard Library ===")
|
||||||
|
|
||||||
// Prelude functions
|
|
||||||
Console.print("identity(42) = " + toString(identity(42)))
|
Console.print("identity(42) = " + toString(identity(42)))
|
||||||
Console.print("not(true) = " + toString(not(true)))
|
Console.print("not(true) = " + toString(not(true)))
|
||||||
Console.print("and(true, false) = " + toString(and(true, false)))
|
Console.print("and(true, false) = " + toString(and(true, false)))
|
||||||
Console.print("or(true, false) = " + toString(or(true, false)))
|
Console.print("or(true, false) = " + toString(or(true, false)))
|
||||||
|
|
||||||
// Option utilities
|
|
||||||
let x = opt.some(10)
|
let x = opt.some(10)
|
||||||
let y = opt.none()
|
let y = opt.none()
|
||||||
Console.print("isSome(Some(10)) = " + toString(opt.isSome(x)))
|
Console.print("isSome(Some(10)) = " + toString(opt.isSome(x)))
|
||||||
|
|||||||
@@ -1,47 +1,31 @@
|
|||||||
// Demonstrating the pipe operator and functional data processing
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// 5 |> double |> addTen |> square = 400
|
|
||||||
// Pipeline result2 = 42
|
|
||||||
// process(1) = 144
|
|
||||||
// process(2) = 196
|
|
||||||
// process(3) = 256
|
|
||||||
// clamped = 0
|
|
||||||
// composed = 121
|
|
||||||
|
|
||||||
// Basic transformations
|
|
||||||
fn double(x: Int): Int = x * 2
|
fn double(x: Int): Int = x * 2
|
||||||
|
|
||||||
fn addTen(x: Int): Int = x + 10
|
fn addTen(x: Int): Int = x + 10
|
||||||
|
|
||||||
fn square(x: Int): Int = x * x
|
fn square(x: Int): Int = x * x
|
||||||
|
|
||||||
fn negate(x: Int): Int = -x
|
fn negate(x: Int): Int = -x
|
||||||
|
|
||||||
// Using the pipe operator for data transformation
|
let result1 = square(addTen(double(5)))
|
||||||
let result1 = 5 |> double |> addTen |> square
|
|
||||||
|
|
||||||
// Chaining multiple operations
|
let result2 = addTen(double(addTen(double(3))))
|
||||||
let result2 = 3 |> double |> addTen |> double |> addTen
|
|
||||||
|
|
||||||
// More complex pipelines
|
fn process(n: Int): Int = square(addTen(double(n)))
|
||||||
fn process(n: Int): Int =
|
|
||||||
n |> double |> addTen |> square
|
|
||||||
|
|
||||||
// Multiple values through same pipeline
|
|
||||||
let a = process(1)
|
let a = process(1)
|
||||||
|
|
||||||
let b = process(2)
|
let b = process(2)
|
||||||
|
|
||||||
let c = process(3)
|
let c = process(3)
|
||||||
|
|
||||||
// Conditional in pipeline
|
fn clampPositive(x: Int): Int = if x < 0 then 0 else x
|
||||||
fn clampPositive(x: Int): Int =
|
|
||||||
if x < 0 then 0 else x
|
|
||||||
|
|
||||||
let clamped = -5 |> double |> clampPositive
|
let clamped = clampPositive(double(-5))
|
||||||
|
|
||||||
// Function composition using pipe
|
|
||||||
fn increment(x: Int): Int = x + 1
|
fn increment(x: Int): Int = x + 1
|
||||||
|
|
||||||
let composed = 5 |> double |> increment |> square
|
let composed = square(increment(double(5)))
|
||||||
|
|
||||||
// Print results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("5 |> double |> addTen |> square = " + toString(result1))
|
Console.print("5 |> double |> addTen |> square = " + toString(result1))
|
||||||
Console.print("Pipeline result2 = " + toString(result2))
|
Console.print("Pipeline result2 = " + toString(result2))
|
||||||
|
|||||||
@@ -1,36 +1,9 @@
|
|||||||
// PostgreSQL Database Example
|
fn jsonStr(key: String, value: String): String = "\"" + key + "\":\"" + value + "\""
|
||||||
//
|
|
||||||
// Demonstrates the Postgres effect for database operations.
|
|
||||||
//
|
|
||||||
// Prerequisites:
|
|
||||||
// - PostgreSQL server running locally
|
|
||||||
// - Database 'testdb' created
|
|
||||||
// - User 'testuser' with password 'testpass'
|
|
||||||
//
|
|
||||||
// To set up:
|
|
||||||
// createdb testdb
|
|
||||||
// psql testdb -c "CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, email TEXT);"
|
|
||||||
//
|
|
||||||
// Run with: lux examples/postgres_demo.lux
|
|
||||||
|
|
||||||
// ============================================================
|
fn jsonNum(key: String, value: Int): String = "\"" + key + "\":" + toString(value)
|
||||||
// Helper Functions
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn jsonStr(key: String, value: String): String =
|
fn jsonObj(content: String): String = toString(" + content + ")
|
||||||
"\"" + key + "\":\"" + value + "\""
|
|
||||||
|
|
||||||
fn jsonNum(key: String, value: Int): String =
|
|
||||||
"\"" + key + "\":" + toString(value)
|
|
||||||
|
|
||||||
fn jsonObj(content: String): String =
|
|
||||||
"{" + content + "}"
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Database Operations
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Insert a user
|
|
||||||
fn insertUser(connId: Int, name: String, email: String): Int with {Console, Postgres} = {
|
fn insertUser(connId: Int, name: String, email: String): Int with {Console, Postgres} = {
|
||||||
let sql = "INSERT INTO users (name, email) VALUES ('" + name + "', '" + email + "') RETURNING id"
|
let sql = "INSERT INTO users (name, email) VALUES ('" + name + "', '" + email + "') RETURNING id"
|
||||||
Console.print("Inserting user: " + name)
|
Console.print("Inserting user: " + name)
|
||||||
@@ -42,31 +15,28 @@ fn insertUser(connId: Int, name: String, email: String): Int with {Console, Post
|
|||||||
None => {
|
None => {
|
||||||
Console.print(" Insert failed")
|
Console.print(" Insert failed")
|
||||||
-1
|
-1
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all users
|
|
||||||
fn getUsers(connId: Int): Unit with {Console, Postgres} = {
|
fn getUsers(connId: Int): Unit with {Console, Postgres} = {
|
||||||
Console.print("Fetching all users...")
|
Console.print("Fetching all users...")
|
||||||
let rows = Postgres.query(connId, "SELECT id, name, email FROM users ORDER BY id")
|
let rows = Postgres.query(connId, "SELECT id, name, email FROM users ORDER BY id")
|
||||||
Console.print(" Found " + toString(List.length(rows)) + " users:")
|
Console.print(" Found " + toString(List.length(rows)) + " users:")
|
||||||
List.forEach(rows, fn(row: { id: Int, name: String, email: String }): Unit with {Console} => {
|
List.forEach(rows, fn(row: { id: Int, name: String, email: String }): Unit => {
|
||||||
Console.print(" - " + toString(row.id) + ": " + row.name + " <" + row.email + ">")
|
Console.print(" - " + toString(row.id) + ": " + row.name + " <" + row.email + ">")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user by ID
|
|
||||||
fn getUserById(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
fn getUserById(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
||||||
let sql = "SELECT id, name, email FROM users WHERE id = " + toString(id)
|
let sql = "SELECT id, name, email FROM users WHERE id = " + toString(id)
|
||||||
Console.print("Looking up user " + toString(id) + "...")
|
Console.print("Looking up user " + toString(id) + "...")
|
||||||
match Postgres.queryOne(connId, sql) {
|
match Postgres.queryOne(connId, sql) {
|
||||||
Some(row) => Console.print(" Found: " + row.name + " <" + row.email + ">"),
|
Some(row) => Console.print(" Found: " + row.name + " <" + row.email + ">"),
|
||||||
None => Console.print(" User not found")
|
None => Console.print(" User not found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update user email
|
|
||||||
fn updateUserEmail(connId: Int, id: Int, newEmail: String): Unit with {Console, Postgres} = {
|
fn updateUserEmail(connId: Int, id: Int, newEmail: String): Unit with {Console, Postgres} = {
|
||||||
let sql = "UPDATE users SET email = '" + newEmail + "' WHERE id = " + toString(id)
|
let sql = "UPDATE users SET email = '" + newEmail + "' WHERE id = " + toString(id)
|
||||||
Console.print("Updating user " + toString(id) + " email to " + newEmail)
|
Console.print("Updating user " + toString(id) + " email to " + newEmail)
|
||||||
@@ -74,7 +44,6 @@ fn updateUserEmail(connId: Int, id: Int, newEmail: String): Unit with {Console,
|
|||||||
Console.print(" Rows affected: " + toString(affected))
|
Console.print(" Rows affected: " + toString(affected))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete user
|
|
||||||
fn deleteUser(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
fn deleteUser(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
||||||
let sql = "DELETE FROM users WHERE id = " + toString(id)
|
let sql = "DELETE FROM users WHERE id = " + toString(id)
|
||||||
Console.print("Deleting user " + toString(id))
|
Console.print("Deleting user " + toString(id))
|
||||||
@@ -82,104 +51,63 @@ fn deleteUser(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
|||||||
Console.print(" Rows affected: " + toString(affected))
|
Console.print(" Rows affected: " + toString(affected))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Transaction Example
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn transactionDemo(connId: Int): Unit with {Console, Postgres} = {
|
fn transactionDemo(connId: Int): Unit with {Console, Postgres} = {
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("=== Transaction Demo ===")
|
Console.print("=== Transaction Demo ===")
|
||||||
|
|
||||||
// Start transaction
|
|
||||||
Console.print("Beginning transaction...")
|
Console.print("Beginning transaction...")
|
||||||
Postgres.beginTx(connId)
|
Postgres.beginTx(connId)
|
||||||
|
|
||||||
// Make some changes
|
|
||||||
insertUser(connId, "TxUser1", "tx1@example.com")
|
insertUser(connId, "TxUser1", "tx1@example.com")
|
||||||
insertUser(connId, "TxUser2", "tx2@example.com")
|
insertUser(connId, "TxUser2", "tx2@example.com")
|
||||||
|
|
||||||
// Show users before commit
|
|
||||||
Console.print("Users before commit:")
|
Console.print("Users before commit:")
|
||||||
getUsers(connId)
|
getUsers(connId)
|
||||||
|
|
||||||
// Commit the transaction
|
|
||||||
Console.print("Committing transaction...")
|
Console.print("Committing transaction...")
|
||||||
Postgres.commit(connId)
|
Postgres.commit(connId)
|
||||||
|
|
||||||
Console.print("Transaction committed!")
|
Console.print("Transaction committed!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Main
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn main(): Unit with {Console, Postgres} = {
|
fn main(): Unit with {Console, Postgres} = {
|
||||||
Console.print("========================================")
|
Console.print("========================================")
|
||||||
Console.print(" PostgreSQL Demo")
|
Console.print(" PostgreSQL Demo")
|
||||||
Console.print("========================================")
|
Console.print("========================================")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Connect to database
|
|
||||||
Console.print("Connecting to PostgreSQL...")
|
Console.print("Connecting to PostgreSQL...")
|
||||||
let connStr = "host=localhost user=testuser password=testpass dbname=testdb"
|
let connStr = "host=localhost user=testuser password=testpass dbname=testdb"
|
||||||
let connId = Postgres.connect(connStr)
|
let connId = Postgres.connect(connStr)
|
||||||
Console.print("Connected! Connection ID: " + toString(connId))
|
Console.print("Connected! Connection ID: " + toString(connId))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Create table if not exists
|
|
||||||
Console.print("Creating users table...")
|
Console.print("Creating users table...")
|
||||||
Postgres.execute(connId, "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL)")
|
Postgres.execute(connId, "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL)")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Clear table for demo
|
|
||||||
Console.print("Clearing existing data...")
|
Console.print("Clearing existing data...")
|
||||||
Postgres.execute(connId, "DELETE FROM users")
|
Postgres.execute(connId, "DELETE FROM users")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Insert some users
|
|
||||||
Console.print("=== Inserting Users ===")
|
Console.print("=== Inserting Users ===")
|
||||||
let id1 = insertUser(connId, "Alice", "alice@example.com")
|
let id1 = insertUser(connId, "Alice", "alice@example.com")
|
||||||
let id2 = insertUser(connId, "Bob", "bob@example.com")
|
let id2 = insertUser(connId, "Bob", "bob@example.com")
|
||||||
let id3 = insertUser(connId, "Charlie", "charlie@example.com")
|
let id3 = insertUser(connId, "Charlie", "charlie@example.com")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Query all users
|
|
||||||
Console.print("=== All Users ===")
|
Console.print("=== All Users ===")
|
||||||
getUsers(connId)
|
getUsers(connId)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Query single user
|
|
||||||
Console.print("=== Single User Lookup ===")
|
Console.print("=== Single User Lookup ===")
|
||||||
getUserById(connId, id2)
|
getUserById(connId, id2)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Update user
|
|
||||||
Console.print("=== Update User ===")
|
Console.print("=== Update User ===")
|
||||||
updateUserEmail(connId, id2, "bob.new@example.com")
|
updateUserEmail(connId, id2, "bob.new@example.com")
|
||||||
getUserById(connId, id2)
|
getUserById(connId, id2)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Delete user
|
|
||||||
Console.print("=== Delete User ===")
|
Console.print("=== Delete User ===")
|
||||||
deleteUser(connId, id3)
|
deleteUser(connId, id3)
|
||||||
getUsers(connId)
|
getUsers(connId)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Transaction demo
|
|
||||||
transactionDemo(connId)
|
transactionDemo(connId)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Final state
|
|
||||||
Console.print("=== Final State ===")
|
Console.print("=== Final State ===")
|
||||||
getUsers(connId)
|
getUsers(connId)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Close connection
|
|
||||||
Console.print("Closing connection...")
|
Console.print("Closing connection...")
|
||||||
Postgres.close(connId)
|
Postgres.close(connId)
|
||||||
Console.print("Done!")
|
Console.print("Done!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: This will fail if PostgreSQL is not running
|
|
||||||
// To test the syntax only, you can comment out the last line
|
|
||||||
let output = run main() with {}
|
let output = run main() with {}
|
||||||
|
|||||||
@@ -1,18 +1,6 @@
|
|||||||
// Property-Based Testing Example
|
|
||||||
//
|
|
||||||
// This example demonstrates property-based testing in Lux,
|
|
||||||
// where we verify properties hold for randomly generated inputs.
|
|
||||||
//
|
|
||||||
// Run with: lux examples/property_testing.lux
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Generator Functions (using Random effect)
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
let CHARS = "abcdefghijklmnopqrstuvwxyz"
|
let CHARS = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
fn genInt(min: Int, max: Int): Int with {Random} =
|
fn genInt(min: Int, max: Int): Int with {Random} = Random.int(min, max)
|
||||||
Random.int(min, max)
|
|
||||||
|
|
||||||
fn genIntList(min: Int, max: Int, maxLen: Int): List<Int> with {Random} = {
|
fn genIntList(min: Int, max: Int, maxLen: Int): List<Int> with {Random} = {
|
||||||
let len = Random.int(0, maxLen)
|
let len = Random.int(0, maxLen)
|
||||||
@@ -20,10 +8,7 @@ fn genIntList(min: Int, max: Int, maxLen: Int): List<Int> with {Random} = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn genIntListHelper(min: Int, max: Int, len: Int): List<Int> with {Random} = {
|
fn genIntListHelper(min: Int, max: Int, len: Int): List<Int> with {Random} = {
|
||||||
if len <= 0 then
|
if len <= 0 then [] else List.concat([Random.int(min, max)], genIntListHelper(min, max, len - 1))
|
||||||
[]
|
|
||||||
else
|
|
||||||
List.concat([Random.int(min, max)], genIntListHelper(min, max, len - 1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genChar(): String with {Random} = {
|
fn genChar(): String with {Random} = {
|
||||||
@@ -37,76 +22,52 @@ fn genString(maxLen: Int): String with {Random} = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn genStringHelper(len: Int): String with {Random} = {
|
fn genStringHelper(len: Int): String with {Random} = {
|
||||||
if len <= 0 then
|
if len <= 0 then "" else genChar() + genStringHelper(len - 1)
|
||||||
""
|
|
||||||
else
|
|
||||||
genChar() + genStringHelper(len - 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Test Runner State
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn printResult(name: String, passed: Bool, count: Int): Unit with {Console} = {
|
fn printResult(name: String, passed: Bool, count: Int): Unit with {Console} = {
|
||||||
if passed then
|
if passed then Console.print(" PASS " + name + " (" + toString(count) + " tests)") else Console.print(" FAIL " + name)
|
||||||
Console.print(" PASS " + name + " (" + toString(count) + " tests)")
|
|
||||||
else
|
|
||||||
Console.print(" FAIL " + name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Property Tests
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Test: List reverse is involutive
|
|
||||||
fn testReverseInvolutive(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testReverseInvolutive(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("reverse(reverse(xs)) == xs", true, count)
|
printResult("reverse(reverse(xs)) == xs", true, count)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let xs = genIntList(0, 100, 20)
|
let xs = genIntList(0, 100, 20)
|
||||||
if List.reverse(List.reverse(xs)) == xs then
|
if List.reverse(List.reverse(xs)) == xs then testReverseInvolutive(n - 1, count) else {
|
||||||
testReverseInvolutive(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("reverse(reverse(xs)) == xs", false, count - n + 1)
|
printResult("reverse(reverse(xs)) == xs", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: List reverse preserves length
|
|
||||||
fn testReverseLength(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testReverseLength(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("length(reverse(xs)) == length(xs)", true, count)
|
printResult("length(reverse(xs)) == length(xs)", true, count)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let xs = genIntList(0, 100, 20)
|
let xs = genIntList(0, 100, 20)
|
||||||
if List.length(List.reverse(xs)) == List.length(xs) then
|
if List.length(List.reverse(xs)) == List.length(xs) then testReverseLength(n - 1, count) else {
|
||||||
testReverseLength(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("length(reverse(xs)) == length(xs)", false, count - n + 1)
|
printResult("length(reverse(xs)) == length(xs)", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: List map preserves length
|
|
||||||
fn testMapLength(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testMapLength(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("length(map(xs, f)) == length(xs)", true, count)
|
printResult("length(map(xs, f)) == length(xs)", true, count)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let xs = genIntList(0, 100, 20)
|
let xs = genIntList(0, 100, 20)
|
||||||
if List.length(List.map(xs, fn(x) => x * 2)) == List.length(xs) then
|
if List.length(List.map(xs, fn(x: _) => x * 2)) == List.length(xs) then testMapLength(n - 1, count) else {
|
||||||
testMapLength(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("length(map(xs, f)) == length(xs)", false, count - n + 1)
|
printResult("length(map(xs, f)) == length(xs)", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: List concat length is sum
|
|
||||||
fn testConcatLength(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testConcatLength(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("length(xs ++ ys) == length(xs) + length(ys)", true, count)
|
printResult("length(xs ++ ys) == length(xs) + length(ys)", true, count)
|
||||||
@@ -114,16 +75,13 @@ fn testConcatLength(n: Int, count: Int): Bool with {Console, Random} = {
|
|||||||
} else {
|
} else {
|
||||||
let xs = genIntList(0, 50, 10)
|
let xs = genIntList(0, 50, 10)
|
||||||
let ys = genIntList(0, 50, 10)
|
let ys = genIntList(0, 50, 10)
|
||||||
if List.length(List.concat(xs, ys)) == List.length(xs) + List.length(ys) then
|
if List.length(List.concat(xs, ys)) == List.length(xs) + List.length(ys) then testConcatLength(n - 1, count) else {
|
||||||
testConcatLength(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("length(xs ++ ys) == length(xs) + length(ys)", false, count - n + 1)
|
printResult("length(xs ++ ys) == length(xs) + length(ys)", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: Addition is commutative
|
|
||||||
fn testAddCommutative(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testAddCommutative(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("a + b == b + a", true, count)
|
printResult("a + b == b + a", true, count)
|
||||||
@@ -131,16 +89,13 @@ fn testAddCommutative(n: Int, count: Int): Bool with {Console, Random} = {
|
|||||||
} else {
|
} else {
|
||||||
let a = genInt(-1000, 1000)
|
let a = genInt(-1000, 1000)
|
||||||
let b = genInt(-1000, 1000)
|
let b = genInt(-1000, 1000)
|
||||||
if a + b == b + a then
|
if a + b == b + a then testAddCommutative(n - 1, count) else {
|
||||||
testAddCommutative(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("a + b == b + a", false, count - n + 1)
|
printResult("a + b == b + a", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: Multiplication is associative
|
|
||||||
fn testMulAssociative(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testMulAssociative(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("(a * b) * c == a * (b * c)", true, count)
|
printResult("(a * b) * c == a * (b * c)", true, count)
|
||||||
@@ -149,16 +104,13 @@ fn testMulAssociative(n: Int, count: Int): Bool with {Console, Random} = {
|
|||||||
let a = genInt(-100, 100)
|
let a = genInt(-100, 100)
|
||||||
let b = genInt(-100, 100)
|
let b = genInt(-100, 100)
|
||||||
let c = genInt(-100, 100)
|
let c = genInt(-100, 100)
|
||||||
if (a * b) * c == a * (b * c) then
|
if a * b * c == a * b * c then testMulAssociative(n - 1, count) else {
|
||||||
testMulAssociative(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("(a * b) * c == a * (b * c)", false, count - n + 1)
|
printResult("(a * b) * c == a * (b * c)", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: String concat length is sum
|
|
||||||
fn testStringConcatLength(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testStringConcatLength(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("length(s1 + s2) == length(s1) + length(s2)", true, count)
|
printResult("length(s1 + s2) == length(s1) + length(s2)", true, count)
|
||||||
@@ -166,67 +118,52 @@ fn testStringConcatLength(n: Int, count: Int): Bool with {Console, Random} = {
|
|||||||
} else {
|
} else {
|
||||||
let s1 = genString(10)
|
let s1 = genString(10)
|
||||||
let s2 = genString(10)
|
let s2 = genString(10)
|
||||||
if String.length(s1 + s2) == String.length(s1) + String.length(s2) then
|
if String.length(s1 + s2) == String.length(s1) + String.length(s2) then testStringConcatLength(n - 1, count) else {
|
||||||
testStringConcatLength(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("length(s1 + s2) == length(s1) + length(s2)", false, count - n + 1)
|
printResult("length(s1 + s2) == length(s1) + length(s2)", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: Zero is identity for addition
|
|
||||||
fn testAddIdentity(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testAddIdentity(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("x + 0 == x && 0 + x == x", true, count)
|
printResult("x + 0 == x && 0 + x == x", true, count)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let x = genInt(-10000, 10000)
|
let x = genInt(-10000, 10000)
|
||||||
if x + 0 == x && 0 + x == x then
|
if x + 0 == x && 0 + x == x then testAddIdentity(n - 1, count) else {
|
||||||
testAddIdentity(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("x + 0 == x && 0 + x == x", false, count - n + 1)
|
printResult("x + 0 == x && 0 + x == x", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: Filter reduces or maintains length
|
|
||||||
fn testFilterLength(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testFilterLength(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("length(filter(xs, p)) <= length(xs)", true, count)
|
printResult("length(filter(xs, p)) <= length(xs)", true, count)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let xs = genIntList(0, 100, 20)
|
let xs = genIntList(0, 100, 20)
|
||||||
if List.length(List.filter(xs, fn(x) => x > 50)) <= List.length(xs) then
|
if List.length(List.filter(xs, fn(x: _) => x > 50)) <= List.length(xs) then testFilterLength(n - 1, count) else {
|
||||||
testFilterLength(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("length(filter(xs, p)) <= length(xs)", false, count - n + 1)
|
printResult("length(filter(xs, p)) <= length(xs)", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: Empty list is identity for concat
|
|
||||||
fn testConcatIdentity(n: Int, count: Int): Bool with {Console, Random} = {
|
fn testConcatIdentity(n: Int, count: Int): Bool with {Console, Random} = {
|
||||||
if n <= 0 then {
|
if n <= 0 then {
|
||||||
printResult("concat(xs, []) == xs && concat([], xs) == xs", true, count)
|
printResult("concat(xs, []) == xs && concat([], xs) == xs", true, count)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let xs = genIntList(0, 100, 10)
|
let xs = genIntList(0, 100, 10)
|
||||||
if List.concat(xs, []) == xs && List.concat([], xs) == xs then
|
if List.concat(xs, []) == xs && List.concat([], xs) == xs then testConcatIdentity(n - 1, count) else {
|
||||||
testConcatIdentity(n - 1, count)
|
|
||||||
else {
|
|
||||||
printResult("concat(xs, []) == xs && concat([], xs) == xs", false, count - n + 1)
|
printResult("concat(xs, []) == xs && concat([], xs) == xs", false, count - n + 1)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Main
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn main(): Unit with {Console, Random} = {
|
fn main(): Unit with {Console, Random} = {
|
||||||
Console.print("========================================")
|
Console.print("========================================")
|
||||||
Console.print(" Property-Based Testing Demo")
|
Console.print(" Property-Based Testing Demo")
|
||||||
@@ -234,7 +171,6 @@ fn main(): Unit with {Console, Random} = {
|
|||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("Running 100 iterations per property...")
|
Console.print("Running 100 iterations per property...")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
testReverseInvolutive(100, 100)
|
testReverseInvolutive(100, 100)
|
||||||
testReverseLength(100, 100)
|
testReverseLength(100, 100)
|
||||||
testMapLength(100, 100)
|
testMapLength(100, 100)
|
||||||
@@ -245,7 +181,6 @@ fn main(): Unit with {Console, Random} = {
|
|||||||
testAddIdentity(100, 100)
|
testAddIdentity(100, 100)
|
||||||
testFilterLength(100, 100)
|
testFilterLength(100, 100)
|
||||||
testConcatIdentity(100, 100)
|
testConcatIdentity(100, 100)
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("========================================")
|
Console.print("========================================")
|
||||||
Console.print(" All property tests completed!")
|
Console.print(" All property tests completed!")
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
// Demonstrating Random and Time effects in Lux
|
|
||||||
//
|
|
||||||
// Expected output (values will vary):
|
|
||||||
// Rolling dice...
|
|
||||||
// Die 1: <random 1-6>
|
|
||||||
// Die 2: <random 1-6>
|
|
||||||
// Die 3: <random 1-6>
|
|
||||||
// Coin flip: <true/false>
|
|
||||||
// Random float: <0.0-1.0>
|
|
||||||
// Current time: <timestamp>
|
|
||||||
|
|
||||||
// Roll a single die (1-6)
|
|
||||||
fn rollDie(): Int with {Random} = Random.int(1, 6)
|
fn rollDie(): Int with {Random} = Random.int(1, 6)
|
||||||
|
|
||||||
// Roll multiple dice and print results
|
|
||||||
fn rollDice(count: Int): Unit with {Random, Console} = {
|
fn rollDice(count: Int): Unit with {Random, Console} = {
|
||||||
if count > 0 then {
|
if count > 0 then {
|
||||||
let value = rollDie()
|
let value = rollDie()
|
||||||
@@ -23,17 +10,13 @@ fn rollDice(count: Int): Unit with {Random, Console} = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main function demonstrating random effects
|
|
||||||
fn main(): Unit with {Random, Console, Time} = {
|
fn main(): Unit with {Random, Console, Time} = {
|
||||||
Console.print("Rolling dice...")
|
Console.print("Rolling dice...")
|
||||||
rollDice(3)
|
rollDice(3)
|
||||||
|
|
||||||
let coin = Random.bool()
|
let coin = Random.bool()
|
||||||
Console.print("Coin flip: " + toString(coin))
|
Console.print("Coin flip: " + toString(coin))
|
||||||
|
|
||||||
let f = Random.float()
|
let f = Random.float()
|
||||||
Console.print("Random float: " + toString(f))
|
Console.print("Random float: " + toString(f))
|
||||||
|
|
||||||
let now = Time.now()
|
let now = Time.now()
|
||||||
Console.print("Current time: " + toString(now))
|
Console.print("Current time: " + toString(now))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +1,41 @@
|
|||||||
// Schema Evolution Demo
|
type User = {
|
||||||
// Demonstrates version tracking and automatic migrations
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// PART 1: Type-Declared Migrations
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Define a versioned type with a migration from v1 to v2
|
|
||||||
type User @v2 {
|
|
||||||
name: String,
|
name: String,
|
||||||
email: String,
|
email: String,
|
||||||
|
|
||||||
// Migration from v1: add default email
|
|
||||||
from @v1 = { name: old.name, email: "unknown@example.com" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a v1 user
|
|
||||||
let v1_user = Schema.versioned("User", 1, { name: "Alice" })
|
let v1_user = Schema.versioned("User", 1, { name: "Alice" })
|
||||||
let v1_version = Schema.getVersion(v1_user) // 1
|
|
||||||
|
|
||||||
// Migrate to v2 - uses the declared migration automatically
|
let v1_version = Schema.getVersion(v1_user)
|
||||||
|
|
||||||
let v2_user = Schema.migrate(v1_user, 2)
|
let v2_user = Schema.migrate(v1_user, 2)
|
||||||
let v2_version = Schema.getVersion(v2_user) // 2
|
|
||||||
|
|
||||||
// ============================================================
|
let v2_version = Schema.getVersion(v2_user)
|
||||||
// PART 2: Runtime Schema Operations (separate type)
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Create versioned values for a different type (no migration)
|
|
||||||
let config1 = Schema.versioned("Config", 1, "debug")
|
let config1 = Schema.versioned("Config", 1, "debug")
|
||||||
|
|
||||||
let config2 = Schema.versioned("Config", 2, "release")
|
let config2 = Schema.versioned("Config", 2, "release")
|
||||||
|
|
||||||
// Check versions
|
let c1 = Schema.getVersion(config1)
|
||||||
let c1 = Schema.getVersion(config1) // 1
|
|
||||||
let c2 = Schema.getVersion(config2) // 2
|
let c2 = Schema.getVersion(config2)
|
||||||
|
|
||||||
// Migrate config (auto-migration since no explicit migration defined)
|
|
||||||
let upgradedConfig = Schema.migrate(config1, 2)
|
let upgradedConfig = Schema.migrate(config1, 2)
|
||||||
let upgradedConfigVersion = Schema.getVersion(upgradedConfig) // 2
|
|
||||||
|
|
||||||
// ============================================================
|
let upgradedConfigVersion = Schema.getVersion(upgradedConfig)
|
||||||
// PART 2: Practical Example - API Versioning
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Simulate different API response versions
|
fn createResponseV1(data: String): { version: Int, payload: String } = { version: 1, payload: data }
|
||||||
fn createResponseV1(data: String): { version: Int, payload: String } =
|
|
||||||
{ version: 1, payload: data }
|
|
||||||
|
|
||||||
fn createResponseV2(data: String, timestamp: Int): { version: Int, payload: String, meta: { ts: Int } } =
|
fn createResponseV2(data: String, timestamp: Int): { version: Int, payload: String, meta: { ts: Int } } = { version: 2, payload: data, meta: { ts: timestamp } }
|
||||||
{ version: 2, payload: data, meta: { ts: timestamp } }
|
|
||||||
|
|
||||||
// Version-aware processing
|
fn getPayload(response: { version: Int, payload: String }): String = response.payload
|
||||||
fn getPayload(response: { version: Int, payload: String }): String =
|
|
||||||
response.payload
|
|
||||||
|
|
||||||
let resp1 = createResponseV1("Hello")
|
let resp1 = createResponseV1("Hello")
|
||||||
|
|
||||||
let resp2 = createResponseV2("World", 1234567890)
|
let resp2 = createResponseV2("World", 1234567890)
|
||||||
|
|
||||||
let payload1 = getPayload(resp1)
|
let payload1 = getPayload(resp1)
|
||||||
let payload2 = resp2.payload
|
|
||||||
|
|
||||||
// ============================================================
|
let payload2 = resp2.payload
|
||||||
// RESULTS
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== Schema Evolution Demo ===")
|
Console.print("=== Schema Evolution Demo ===")
|
||||||
|
|||||||
@@ -1,46 +1,31 @@
|
|||||||
// Shell/Process example - demonstrates the Process effect
|
|
||||||
//
|
|
||||||
// This script runs shell commands and uses environment variables
|
|
||||||
|
|
||||||
fn main(): Unit with {Process, Console} = {
|
fn main(): Unit with {Process, Console} = {
|
||||||
Console.print("=== Lux Shell Example ===")
|
Console.print("=== Lux Shell Example ===")
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Get current working directory
|
|
||||||
let cwd = Process.cwd()
|
let cwd = Process.cwd()
|
||||||
Console.print("Current directory: " + cwd)
|
Console.print("Current directory: " + cwd)
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Get environment variables
|
|
||||||
Console.print("Environment variables:")
|
Console.print("Environment variables:")
|
||||||
match Process.env("USER") {
|
match Process.env("USER") {
|
||||||
Some(user) => Console.print(" USER: " + user),
|
Some(user) => Console.print(" USER: " + user),
|
||||||
None => Console.print(" USER: (not set)")
|
None => Console.print(" USER: (not set)"),
|
||||||
}
|
}
|
||||||
match Process.env("HOME") {
|
match Process.env("HOME") {
|
||||||
Some(home) => Console.print(" HOME: " + home),
|
Some(home) => Console.print(" HOME: " + home),
|
||||||
None => Console.print(" HOME: (not set)")
|
None => Console.print(" HOME: (not set)"),
|
||||||
}
|
}
|
||||||
match Process.env("SHELL") {
|
match Process.env("SHELL") {
|
||||||
Some(shell) => Console.print(" SHELL: " + shell),
|
Some(shell) => Console.print(" SHELL: " + shell),
|
||||||
None => Console.print(" SHELL: (not set)")
|
None => Console.print(" SHELL: (not set)"),
|
||||||
}
|
}
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Run shell commands
|
|
||||||
Console.print("Running shell commands:")
|
Console.print("Running shell commands:")
|
||||||
|
|
||||||
let date = Process.exec("date")
|
let date = Process.exec("date")
|
||||||
Console.print(" date: " + String.trim(date))
|
Console.print(" date: " + String.trim(date))
|
||||||
|
|
||||||
let kernel = Process.exec("uname -r")
|
let kernel = Process.exec("uname -r")
|
||||||
Console.print(" kernel: " + String.trim(kernel))
|
Console.print(" kernel: " + String.trim(kernel))
|
||||||
|
|
||||||
let files = Process.exec("ls examples/*.lux | wc -l")
|
let files = Process.exec("ls examples/*.lux | wc -l")
|
||||||
Console.print(" .lux files in examples/: " + String.trim(files))
|
Console.print(" .lux files in examples/: " + String.trim(files))
|
||||||
Console.print("")
|
Console.print("")
|
||||||
|
|
||||||
// Command line arguments
|
|
||||||
Console.print("Command line arguments:")
|
Console.print("Command line arguments:")
|
||||||
let args = Process.args()
|
let args = Process.args()
|
||||||
let argCount = List.length(args)
|
let argCount = List.length(args)
|
||||||
@@ -50,7 +35,7 @@ fn main(): Unit with {Process, Console} = {
|
|||||||
Console.print(" Count: " + toString(argCount))
|
Console.print(" Count: " + toString(argCount))
|
||||||
match List.head(args) {
|
match List.head(args) {
|
||||||
Some(first) => Console.print(" First: " + first),
|
Some(first) => Console.print(" First: " + first),
|
||||||
None => Console.print(" First: (empty)")
|
None => Console.print(" First: (empty)"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
// 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 {
|
effect Config {
|
||||||
fn get(key: String): String
|
fn get(key: String): String
|
||||||
}
|
}
|
||||||
@@ -25,14 +13,13 @@ fn configure(): String with {Config, Console} = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler envConfig: Config {
|
handler envConfig: Config {
|
||||||
fn get(key) =
|
fn get(key) = if key == "api_url" then resume("https://api.example.com") else if key == "timeout" then resume("30") else resume("unknown")
|
||||||
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} = {
|
fn main(): Unit with {Console} = {
|
||||||
let result = run configure() with { Config = envConfig }
|
let result = run configure() with {
|
||||||
|
Config = envConfig,
|
||||||
|
}
|
||||||
Console.print(result)
|
Console.print(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
// 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 {
|
effect Log {
|
||||||
fn info(msg: String): Unit
|
fn info(msg: String): Unit
|
||||||
fn debug(msg: String): Unit
|
fn debug(msg: String): Unit
|
||||||
@@ -26,18 +14,22 @@ fn computation(): Int with {Log} = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler consoleLogger: Log {
|
handler consoleLogger: Log {
|
||||||
fn info(msg) = {
|
fn info(msg) =
|
||||||
|
{
|
||||||
Console.print("[INFO] " + msg)
|
Console.print("[INFO] " + msg)
|
||||||
resume(())
|
resume(())
|
||||||
}
|
}
|
||||||
fn debug(msg) = {
|
fn debug(msg) =
|
||||||
|
{
|
||||||
Console.print("[DEBUG] " + msg)
|
Console.print("[DEBUG] " + msg)
|
||||||
resume(())
|
resume(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let result = run computation() with { Log = consoleLogger }
|
let result = run computation() with {
|
||||||
|
Log = consoleLogger,
|
||||||
|
}
|
||||||
Console.print("Final: " + toString(result))
|
Console.print("Final: " + toString(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,18 @@
|
|||||||
// 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} = {
|
fn parsePositive(s: String): Int with {Fail, Console} = {
|
||||||
Console.print("Parsing \"" + s + "\"...")
|
Console.print("Parsing \"" + s + "\"...")
|
||||||
if s == "42" then 42
|
if s == "42" then 42 else if s == "100" then 100 else Fail.fail("Invalid number: " + s)
|
||||||
else if s == "100" then 100
|
|
||||||
else Fail.fail("Invalid number: " + s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn safeDivide(a: Int, b: Int): Int with {Fail, Console} = {
|
fn safeDivide(a: Int, b: Int): Int with {Fail, Console} = {
|
||||||
Console.print("Dividing " + toString(a) + " by " + toString(b) + "...")
|
Console.print("Dividing " + toString(a) + " by " + toString(b) + "...")
|
||||||
if b == 0 then Fail.fail("Division by zero")
|
if b == 0 then Fail.fail("Division by zero") else a / b
|
||||||
else a / b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
// These succeed
|
|
||||||
let n1 = run parsePositive("42") with {}
|
let n1 = run parsePositive("42") with {}
|
||||||
Console.print("Result: " + toString(n1))
|
Console.print("Result: " + toString(n1))
|
||||||
|
|
||||||
let n2 = run parsePositive("100") with {}
|
let n2 = run parsePositive("100") with {}
|
||||||
Console.print("Result: " + toString(n2))
|
Console.print("Result: " + toString(n2))
|
||||||
|
|
||||||
let n3 = run safeDivide(100, 4) with {}
|
let n3 = run safeDivide(100, 4) with {}
|
||||||
Console.print("Result: " + toString(n3))
|
Console.print("Result: " + toString(n3))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
// 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 {
|
effect Log {
|
||||||
fn log(msg: String): Unit
|
fn log(msg: String): Unit
|
||||||
}
|
}
|
||||||
@@ -30,7 +17,7 @@ handler consoleLog: Log {
|
|||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
let result = run computation() with {
|
let result = run computation() with {
|
||||||
Log = consoleLog
|
Log = consoleLog,
|
||||||
}
|
}
|
||||||
Console.print("Generated: " + toString(result / 2))
|
Console.print("Generated: " + toString(result / 2))
|
||||||
Console.print("Result: " + toString(result))
|
Console.print("Result: " + toString(result))
|
||||||
|
|||||||
@@ -1,38 +1,19 @@
|
|||||||
// 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 apply(f: fn(Int): Int, x: Int): Int = f(x)
|
||||||
|
|
||||||
fn compose(f: fn(Int): Int, g: fn(Int): Int): fn(Int): Int =
|
fn compose(f: fn(Int): Int, g: fn(Int): Int): fn(Int): Int = fn(x: Int): Int => f(g(x))
|
||||||
fn(x: Int): Int => f(g(x))
|
|
||||||
|
|
||||||
fn square(n: Int): Int = n * n
|
fn square(n: Int): Int = n * n
|
||||||
|
|
||||||
fn cube(n: Int): Int = n * n * n
|
fn cube(n: Int): Int = n * n * n
|
||||||
|
|
||||||
fn makeAdder(n: Int): fn(Int): Int =
|
fn makeAdder(n: Int): fn(Int): Int = fn(x: Int): Int => x + n
|
||||||
fn(x: Int): Int => x + n
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
// Apply functions
|
|
||||||
Console.print("Square of 5: " + toString(apply(square, 5)))
|
Console.print("Square of 5: " + toString(apply(square, 5)))
|
||||||
Console.print("Cube of 3: " + toString(apply(cube, 3)))
|
Console.print("Cube of 3: " + toString(apply(cube, 3)))
|
||||||
|
|
||||||
// Closures
|
|
||||||
let add10 = makeAdder(10)
|
let add10 = makeAdder(10)
|
||||||
Console.print("Add 10 to 5: " + toString(add10(5)))
|
Console.print("Add 10 to 5: " + toString(add10(5)))
|
||||||
Console.print("Add 10 to 20: " + toString(add10(20)))
|
Console.print("Add 10 to 20: " + toString(add10(20)))
|
||||||
|
|
||||||
// Function composition
|
|
||||||
let squareThenCube = compose(cube, square)
|
let squareThenCube = compose(cube, square)
|
||||||
Console.print("Composed: " + toString(squareThenCube(5)))
|
Console.print("Composed: " + toString(squareThenCube(5)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
// 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 =
|
type Expr =
|
||||||
| Num(Int)
|
| Num(Int)
|
||||||
| Add(Expr, Expr)
|
| Add(Expr, Expr)
|
||||||
@@ -22,7 +9,7 @@ fn eval(e: Expr): Int =
|
|||||||
Num(n) => n,
|
Num(n) => n,
|
||||||
Add(a, b) => eval(a) + eval(b),
|
Add(a, b) => eval(a) + eval(b),
|
||||||
Sub(a, b) => eval(a) - eval(b),
|
Sub(a, b) => eval(a) - eval(b),
|
||||||
Mul(a, b) => eval(a) * eval(b)
|
Mul(a, b) => eval(a) * eval(b),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showExpr(e: Expr): String =
|
fn showExpr(e: Expr): String =
|
||||||
@@ -30,7 +17,7 @@ fn showExpr(e: Expr): String =
|
|||||||
Num(n) => toString(n),
|
Num(n) => toString(n),
|
||||||
Add(a, b) => "(" + showExpr(a) + " + " + showExpr(b) + ")",
|
Add(a, b) => "(" + showExpr(a) + " + " + showExpr(b) + ")",
|
||||||
Sub(a, b) => "(" + showExpr(a) + " - " + showExpr(b) + ")",
|
Sub(a, b) => "(" + showExpr(a) + " - " + showExpr(b) + ")",
|
||||||
Mul(a, b) => "(" + showExpr(a) + " * " + showExpr(b) + ")"
|
Mul(a, b) => "(" + showExpr(a) + " * " + showExpr(b) + ")",
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evalAndPrint(e: Expr): Unit with {Console} = {
|
fn evalAndPrint(e: Expr): Unit with {Console} = {
|
||||||
@@ -39,15 +26,10 @@ fn evalAndPrint(e: Expr): Unit with {Console} = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
// (2 + 3)
|
|
||||||
let e1 = Add(Num(2), Num(3))
|
let e1 = Add(Num(2), Num(3))
|
||||||
evalAndPrint(e1)
|
evalAndPrint(e1)
|
||||||
|
|
||||||
// ((1 + 2) * (3 + 4))
|
|
||||||
let e2 = Mul(Add(Num(1), Num(2)), Add(Num(3), Num(4)))
|
let e2 = Mul(Add(Num(1), Num(2)), Add(Num(3), Num(4)))
|
||||||
evalAndPrint(e2)
|
evalAndPrint(e2)
|
||||||
|
|
||||||
// (10 - (2 * 3))
|
|
||||||
let e3 = Sub(Num(10), Mul(Num(2), Num(3)))
|
let e3 = Sub(Num(10), Mul(Num(2), Num(3)))
|
||||||
evalAndPrint(e3)
|
evalAndPrint(e3)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
// Factorial - compute n!
|
fn factorial(n: Int): Int = if n <= 1 then 1 else n * factorial(n - 1)
|
||||||
|
|
||||||
// Recursive version
|
fn factorialTail(n: Int, acc: Int): Int = if n <= 1 then acc else factorialTail(n - 1, n * acc)
|
||||||
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 factorial2(n: Int): Int = factorialTail(n, 1)
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,11 @@
|
|||||||
// FizzBuzz - print numbers 1-100, but:
|
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)
|
||||||
// - 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} =
|
fn printFizzbuzz(i: Int, max: Int): Unit with {Console} =
|
||||||
if i > max then ()
|
if i > max then () else {
|
||||||
else {
|
|
||||||
Console.print(fizzbuzz(i))
|
Console.print(fizzbuzz(i))
|
||||||
printFizzbuzz(i + 1, max)
|
printFizzbuzz(i + 1, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(): Unit with {Console} =
|
fn main(): Unit with {Console} = printFizzbuzz(1, 100)
|
||||||
printFizzbuzz(1, 100)
|
|
||||||
|
|
||||||
let output = run main() with {}
|
let output = run main() with {}
|
||||||
|
|||||||
@@ -1,42 +1,17 @@
|
|||||||
// Number guessing game - demonstrates Random and Console effects
|
fn checkGuess(guess: Int, secret: Int): String = if guess == secret then "Correct" else if guess < secret then "Too low" else "Too high"
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// Welcome to the Guessing Game!
|
|
||||||
// Target number: 42
|
|
||||||
// Simulating guesses...
|
|
||||||
// Guess 50: Too high!
|
|
||||||
// Guess 25: Too low!
|
|
||||||
// Guess 37: Too low!
|
|
||||||
// Guess 43: Too high!
|
|
||||||
// Guess 40: Too low!
|
|
||||||
// Guess 41: Too low!
|
|
||||||
// Guess 42: Correct!
|
|
||||||
// Found in 7 attempts!
|
|
||||||
|
|
||||||
// Game logic - check a guess against the secret
|
|
||||||
fn checkGuess(guess: Int, secret: Int): String =
|
|
||||||
if guess == secret then "Correct"
|
|
||||||
else if guess < secret then "Too low"
|
|
||||||
else "Too high"
|
|
||||||
|
|
||||||
// Binary search simulation to find the number
|
|
||||||
fn binarySearch(low: Int, high: Int, secret: Int, attempts: Int): Int with {Console} = {
|
fn binarySearch(low: Int, high: Int, secret: Int, attempts: Int): Int with {Console} = {
|
||||||
let mid = (low + high) / 2
|
let mid = low + high / 2
|
||||||
let result = checkGuess(mid, secret)
|
let result = checkGuess(mid, secret)
|
||||||
Console.print("Guess " + toString(mid) + ": " + result + "!")
|
Console.print("Guess " + toString(mid) + ": " + result + "!")
|
||||||
|
if result == "Correct" then attempts else if result == "Too low" then binarySearch(mid + 1, high, secret, attempts + 1) else binarySearch(low, mid - 1, secret, attempts + 1)
|
||||||
if result == "Correct" then attempts
|
|
||||||
else if result == "Too low" then binarySearch(mid + 1, high, secret, attempts + 1)
|
|
||||||
else binarySearch(low, mid - 1, secret, attempts + 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("Welcome to the Guessing Game!")
|
Console.print("Welcome to the Guessing Game!")
|
||||||
// Use a fixed "secret" for reproducible output
|
|
||||||
let secret = 42
|
let secret = 42
|
||||||
Console.print("Target number: " + toString(secret))
|
Console.print("Target number: " + toString(secret))
|
||||||
Console.print("Simulating guesses...")
|
Console.print("Simulating guesses...")
|
||||||
|
|
||||||
let attempts = binarySearch(1, 100, secret, 1)
|
let attempts = binarySearch(1, 100, secret, 1)
|
||||||
Console.print("Found in " + toString(attempts) + " attempts!")
|
Console.print("Found in " + toString(attempts) + " attempts!")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
// The classic first program
|
fn main(): Unit with {Console} = Console.print("Hello, World!")
|
||||||
// Expected output: Hello, World!
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} =
|
|
||||||
Console.print("Hello, World!")
|
|
||||||
|
|
||||||
let output = run main() with {}
|
let output = run main() with {}
|
||||||
|
|||||||
@@ -1,25 +1,14 @@
|
|||||||
// Prime number utilities
|
fn isPrime(n: Int): Bool = if n < 2 then false else isPrimeHelper(n, 2)
|
||||||
|
|
||||||
fn isPrime(n: Int): Bool =
|
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)
|
||||||
if n < 2 then false
|
|
||||||
else isPrimeHelper(n, 2)
|
|
||||||
|
|
||||||
fn isPrimeHelper(n: Int, i: Int): Bool =
|
fn findPrimes(count: Int): Unit with {Console} = findPrimesHelper(2, count)
|
||||||
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} =
|
fn findPrimesHelper(current: Int, remaining: Int): Unit with {Console} =
|
||||||
if remaining <= 0 then ()
|
if remaining <= 0 then () else if isPrime(current) then {
|
||||||
else if isPrime(current) then {
|
|
||||||
Console.print(toString(current))
|
Console.print(toString(current))
|
||||||
findPrimesHelper(current + 1, remaining - 1)
|
findPrimesHelper(current + 1, remaining - 1)
|
||||||
}
|
} else findPrimesHelper(current + 1, remaining)
|
||||||
else findPrimesHelper(current + 1, remaining)
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("First 20 prime numbers:")
|
Console.print("First 20 prime numbers:")
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// Standard Library Demo
|
|
||||||
// Demonstrates the built-in modules: List, String, Option, Math
|
|
||||||
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
Console.print("=== List Operations ===")
|
Console.print("=== List Operations ===")
|
||||||
let nums = [1, 2, 3, 4, 5]
|
let nums = [1, 2, 3, 4, 5]
|
||||||
@@ -11,7 +8,6 @@ fn main(): Unit with {Console} = {
|
|||||||
Console.print("Length: " + toString(List.length(nums)))
|
Console.print("Length: " + toString(List.length(nums)))
|
||||||
Console.print("Reversed: " + toString(List.reverse(nums)))
|
Console.print("Reversed: " + toString(List.reverse(nums)))
|
||||||
Console.print("Range 1-5: " + toString(List.range(1, 6)))
|
Console.print("Range 1-5: " + toString(List.range(1, 6)))
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("=== String Operations ===")
|
Console.print("=== String Operations ===")
|
||||||
let text = " Hello, World! "
|
let text = " Hello, World! "
|
||||||
@@ -22,7 +18,6 @@ fn main(): Unit with {Console} = {
|
|||||||
Console.print("Contains 'World': " + toString(String.contains(text, "World")))
|
Console.print("Contains 'World': " + toString(String.contains(text, "World")))
|
||||||
Console.print("Split by comma: " + toString(String.split("a,b,c", ",")))
|
Console.print("Split by comma: " + toString(String.split("a,b,c", ",")))
|
||||||
Console.print("Join with dash: " + String.join(["x", "y", "z"], "-"))
|
Console.print("Join with dash: " + String.join(["x", "y", "z"], "-"))
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("=== Option Operations ===")
|
Console.print("=== Option Operations ===")
|
||||||
let some_val = Some(42)
|
let some_val = Some(42)
|
||||||
@@ -31,7 +26,6 @@ fn main(): Unit with {Console} = {
|
|||||||
Console.print("None mapped: " + toString(Option.map(none_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("Some(42) getOrElse(0): " + toString(Option.getOrElse(some_val, 0)))
|
||||||
Console.print("None getOrElse(0): " + toString(Option.getOrElse(none_val, 0)))
|
Console.print("None getOrElse(0): " + toString(Option.getOrElse(none_val, 0)))
|
||||||
|
|
||||||
Console.print("")
|
Console.print("")
|
||||||
Console.print("=== Math Operations ===")
|
Console.print("=== Math Operations ===")
|
||||||
Console.print("abs(-5): " + toString(Math.abs(-5)))
|
Console.print("abs(-5): " + toString(Math.abs(-5)))
|
||||||
|
|||||||
@@ -1,13 +1,3 @@
|
|||||||
// State machine example using algebraic data types
|
|
||||||
// Demonstrates pattern matching for state transitions
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// Initial light: red
|
|
||||||
// After transition: green
|
|
||||||
// After two transitions: yellow
|
|
||||||
// Door: Closed -> Open -> Closed -> Locked
|
|
||||||
|
|
||||||
// Traffic light state machine
|
|
||||||
type TrafficLight =
|
type TrafficLight =
|
||||||
| Red
|
| Red
|
||||||
| Yellow
|
| Yellow
|
||||||
@@ -17,24 +7,23 @@ fn nextLight(light: TrafficLight): TrafficLight =
|
|||||||
match light {
|
match light {
|
||||||
Red => Green,
|
Red => Green,
|
||||||
Green => Yellow,
|
Green => Yellow,
|
||||||
Yellow => Red
|
Yellow => Red,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canGo(light: TrafficLight): Bool =
|
fn canGo(light: TrafficLight): Bool =
|
||||||
match light {
|
match light {
|
||||||
Green => true,
|
Green => true,
|
||||||
Yellow => false,
|
Yellow => false,
|
||||||
Red => false
|
Red => false,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lightColor(light: TrafficLight): String =
|
fn lightColor(light: TrafficLight): String =
|
||||||
match light {
|
match light {
|
||||||
Red => "red",
|
Red => "red",
|
||||||
Yellow => "yellow",
|
Yellow => "yellow",
|
||||||
Green => "green"
|
Green => "green",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Door state machine
|
|
||||||
type DoorState =
|
type DoorState =
|
||||||
| Open
|
| Open
|
||||||
| Closed
|
| Closed
|
||||||
@@ -52,27 +41,30 @@ fn applyAction(state: DoorState, action: DoorAction): DoorState =
|
|||||||
(Open, CloseDoor) => Closed,
|
(Open, CloseDoor) => Closed,
|
||||||
(Closed, LockDoor) => Locked,
|
(Closed, LockDoor) => Locked,
|
||||||
(Locked, UnlockDoor) => Closed,
|
(Locked, UnlockDoor) => Closed,
|
||||||
_ => state
|
_ => state,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doorStateName(state: DoorState): String =
|
fn doorStateName(state: DoorState): String =
|
||||||
match state {
|
match state {
|
||||||
Open => "Open",
|
Open => "Open",
|
||||||
Closed => "Closed",
|
Closed => "Closed",
|
||||||
Locked => "Locked"
|
Locked => "Locked",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the state machines
|
|
||||||
let light1 = Red
|
let light1 = Red
|
||||||
|
|
||||||
let light2 = nextLight(light1)
|
let light2 = nextLight(light1)
|
||||||
|
|
||||||
let light3 = nextLight(light2)
|
let light3 = nextLight(light2)
|
||||||
|
|
||||||
let door1 = Closed
|
let door1 = Closed
|
||||||
|
|
||||||
let door2 = applyAction(door1, OpenDoor)
|
let door2 = applyAction(door1, OpenDoor)
|
||||||
|
|
||||||
let door3 = applyAction(door2, CloseDoor)
|
let door3 = applyAction(door2, CloseDoor)
|
||||||
|
|
||||||
let door4 = applyAction(door3, LockDoor)
|
let door4 = applyAction(door3, LockDoor)
|
||||||
|
|
||||||
// Print results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("Initial light: " + lightColor(light1))
|
Console.print("Initial light: " + lightColor(light1))
|
||||||
Console.print("After transition: " + lightColor(light2))
|
Console.print("After transition: " + lightColor(light2))
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
// Stress test for RC system with large lists
|
|
||||||
// Tests FBIP optimization with single-owner chains
|
|
||||||
|
|
||||||
fn processChain(n: Int): Int = {
|
fn processChain(n: Int): Int = {
|
||||||
// Single owner chain - FBIP should reuse lists
|
|
||||||
let nums = List.range(1, n)
|
let nums = List.range(1, n)
|
||||||
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
|
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
|
||||||
let filtered = List.filter(doubled, fn(x: Int): Bool => x > n)
|
let filtered = List.filter(doubled, fn(x: Int): Bool => x > n)
|
||||||
@@ -12,13 +8,10 @@ fn processChain(n: Int): Int = {
|
|||||||
|
|
||||||
fn main(): Unit = {
|
fn main(): Unit = {
|
||||||
Console.print("=== RC Stress Test ===")
|
Console.print("=== RC Stress Test ===")
|
||||||
|
|
||||||
// Run multiple iterations of list operations
|
|
||||||
let result1 = processChain(100)
|
let result1 = processChain(100)
|
||||||
let result2 = processChain(200)
|
let result2 = processChain(200)
|
||||||
let result3 = processChain(500)
|
let result3 = processChain(500)
|
||||||
let result4 = processChain(1000)
|
let result4 = processChain(1000)
|
||||||
|
|
||||||
Console.print("Completed 4 chains")
|
Console.print("Completed 4 chains")
|
||||||
Console.print("Sizes: 100, 200, 500, 1000")
|
Console.print("Sizes: 100, 200, 500, 1000")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
// Stress test for RC system WITH shared references
|
|
||||||
// Forces rc>1 path by keeping aliases
|
|
||||||
|
|
||||||
fn processWithAlias(n: Int): Int = {
|
fn processWithAlias(n: Int): Int = {
|
||||||
let nums = List.range(1, n)
|
let nums = List.range(1, n)
|
||||||
let alias = nums // This increments rc, forcing copy path
|
let alias = nums
|
||||||
let _len = List.length(alias) // Use the alias
|
let _len = List.length(alias)
|
||||||
|
|
||||||
// Now nums has rc>1, so map must allocate new
|
|
||||||
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
|
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
|
||||||
let filtered = List.filter(doubled, fn(x: Int): Bool => x > n)
|
let filtered = List.filter(doubled, fn(x: Int): Bool => x > n)
|
||||||
let reversed = List.reverse(filtered)
|
let reversed = List.reverse(filtered)
|
||||||
@@ -15,12 +10,9 @@ fn processWithAlias(n: Int): Int = {
|
|||||||
|
|
||||||
fn main(): Unit = {
|
fn main(): Unit = {
|
||||||
Console.print("=== RC Stress Test (Shared Refs) ===")
|
Console.print("=== RC Stress Test (Shared Refs) ===")
|
||||||
|
|
||||||
// Run multiple iterations with shared references
|
|
||||||
let result1 = processWithAlias(100)
|
let result1 = processWithAlias(100)
|
||||||
let result2 = processWithAlias(200)
|
let result2 = processWithAlias(200)
|
||||||
let result3 = processWithAlias(500)
|
let result3 = processWithAlias(500)
|
||||||
let result4 = processWithAlias(1000)
|
let result4 = processWithAlias(1000)
|
||||||
|
|
||||||
Console.print("Completed 4 chains with shared refs")
|
Console.print("Completed 4 chains with shared refs")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,25 @@
|
|||||||
// Demonstrating tail call optimization (TCO) in Lux
|
fn factorialTCO(n: Int, acc: Int): Int = if n <= 1 then acc else factorialTCO(n - 1, n * acc)
|
||||||
// TCO allows recursive functions to run in constant stack space
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// factorial(20) = 2432902008176640000
|
|
||||||
// fib(30) = 832040
|
|
||||||
// sumTo(1000) = 500500
|
|
||||||
// countdown(10000) completed
|
|
||||||
|
|
||||||
// Factorial with accumulator - tail recursive
|
|
||||||
fn factorialTCO(n: Int, acc: Int): Int =
|
|
||||||
if n <= 1 then acc
|
|
||||||
else factorialTCO(n - 1, n * acc)
|
|
||||||
|
|
||||||
fn factorial(n: Int): Int = factorialTCO(n, 1)
|
fn factorial(n: Int): Int = factorialTCO(n, 1)
|
||||||
|
|
||||||
// Fibonacci with accumulator - tail recursive
|
fn fibTCO(n: Int, a: Int, b: Int): Int = if n <= 0 then a else fibTCO(n - 1, b, a + b)
|
||||||
fn fibTCO(n: Int, a: Int, b: Int): Int =
|
|
||||||
if n <= 0 then a
|
|
||||||
else fibTCO(n - 1, b, a + b)
|
|
||||||
|
|
||||||
fn fib(n: Int): Int = fibTCO(n, 0, 1)
|
fn fib(n: Int): Int = fibTCO(n, 0, 1)
|
||||||
|
|
||||||
// Count down - simple tail recursion
|
fn countdown(n: Int): Int = if n <= 0 then 0 else countdown(n - 1)
|
||||||
fn countdown(n: Int): Int =
|
|
||||||
if n <= 0 then 0
|
|
||||||
else countdown(n - 1)
|
|
||||||
|
|
||||||
// Sum with accumulator - tail recursive
|
fn sumToTCO(n: Int, acc: Int): Int = if n <= 0 then acc else sumToTCO(n - 1, acc + n)
|
||||||
fn sumToTCO(n: Int, acc: Int): Int =
|
|
||||||
if n <= 0 then acc
|
|
||||||
else sumToTCO(n - 1, acc + n)
|
|
||||||
|
|
||||||
fn sumTo(n: Int): Int = sumToTCO(n, 0)
|
fn sumTo(n: Int): Int = sumToTCO(n, 0)
|
||||||
|
|
||||||
// Test the functions
|
|
||||||
let fact20 = factorial(20)
|
let fact20 = factorial(20)
|
||||||
|
|
||||||
let fib30 = fib(30)
|
let fib30 = fib(30)
|
||||||
|
|
||||||
let sum1000 = sumTo(1000)
|
let sum1000 = sumTo(1000)
|
||||||
|
|
||||||
let countResult = countdown(10000)
|
let countResult = countdown(10000)
|
||||||
|
|
||||||
// Print results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("factorial(20) = " + toString(fact20))
|
Console.print("factorial(20) = " + toString(fact20))
|
||||||
Console.print("fib(30) = " + toString(fib30))
|
Console.print("fib(30) = " + toString(fib30))
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
// This test shows FBIP optimization by comparing allocation counts
|
|
||||||
// With FBIP (rc=1): lists are reused in-place
|
|
||||||
// Without FBIP (rc>1): new lists are allocated
|
|
||||||
|
|
||||||
fn main(): Unit = {
|
fn main(): Unit = {
|
||||||
Console.print("=== FBIP Allocation Test ===")
|
Console.print("=== FBIP Allocation Test ===")
|
||||||
|
|
||||||
// Case 1: Single owner (FBIP active) - should reuse list
|
|
||||||
let a = List.range(1, 100)
|
let a = List.range(1, 100)
|
||||||
let b = List.map(a, fn(x: Int): Int => x * 2)
|
let b = List.map(a, fn(x: Int): Int => x * 2)
|
||||||
let c = List.filter(b, fn(x: Int): Bool => x > 50)
|
let c = List.filter(b, fn(x: Int): Bool => x > 50)
|
||||||
let d = List.reverse(c)
|
let d = List.reverse(c)
|
||||||
Console.print("Single owner chain done")
|
Console.print("Single owner chain done")
|
||||||
|
|
||||||
// The allocation count will show FBIP is working
|
|
||||||
// if allocations are low relative to operations performed
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
fn main(): Unit = {
|
fn main(): Unit = {
|
||||||
// Test FBIP without string operations
|
|
||||||
let nums = [1, 2, 3, 4, 5]
|
let nums = [1, 2, 3, 4, 5]
|
||||||
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
|
let doubled = List.map(nums, fn(x: Int): Int => x * 2)
|
||||||
let filtered = List.filter(doubled, fn(x: Int): Bool => x > 4)
|
let filtered = List.filter(doubled, fn(x: Int): Bool => x > 4)
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// List Operations Test Suite
|
|
||||||
// Run with: lux test examples/test_lists.lux
|
|
||||||
|
|
||||||
fn test_list_length(): Unit with {Test} = {
|
fn test_list_length(): Unit with {Test} = {
|
||||||
Test.assertEqual(0, List.length([]))
|
Test.assertEqual(0, List.length([]))
|
||||||
Test.assertEqual(1, List.length([1]))
|
Test.assertEqual(1, List.length([1]))
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// Math Test Suite
|
|
||||||
// Run with: lux test examples/test_math.lux
|
|
||||||
|
|
||||||
fn test_addition(): Unit with {Test} = {
|
fn test_addition(): Unit with {Test} = {
|
||||||
Test.assertEqual(4, 2 + 2)
|
Test.assertEqual(4, 2 + 2)
|
||||||
Test.assertEqual(0, 0 + 0)
|
Test.assertEqual(0, 0 + 0)
|
||||||
|
|||||||
@@ -1,21 +1,10 @@
|
|||||||
// Test demonstrating ownership transfer with aliases
|
|
||||||
// The ownership transfer optimization ensures FBIP still works
|
|
||||||
// even when variables are aliased, because ownership is transferred
|
|
||||||
// rather than reference count being incremented.
|
|
||||||
|
|
||||||
fn main(): Unit = {
|
fn main(): Unit = {
|
||||||
Console.print("=== Ownership Transfer Test ===")
|
Console.print("=== Ownership Transfer Test ===")
|
||||||
|
|
||||||
let a = List.range(1, 100)
|
let a = List.range(1, 100)
|
||||||
// Ownership transfers from 'a' to 'alias', keeping rc=1
|
|
||||||
let alias = a
|
let alias = a
|
||||||
let len1 = List.length(alias)
|
let len1 = List.length(alias)
|
||||||
|
|
||||||
// Since ownership transferred, 'a' still has rc=1
|
|
||||||
// FBIP can still optimize map/filter/reverse
|
|
||||||
let b = List.map(a, fn(x: Int): Int => x * 2)
|
let b = List.map(a, fn(x: Int): Int => x * 2)
|
||||||
let c = List.filter(b, fn(x: Int): Bool => x > 50)
|
let c = List.filter(b, fn(x: Int): Bool => x > 50)
|
||||||
let d = List.reverse(c)
|
let d = List.reverse(c)
|
||||||
|
|
||||||
Console.print("Ownership transfer chain done")
|
Console.print("Ownership transfer chain done")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
fn main(): Unit = {
|
fn main(): Unit = {
|
||||||
Console.print("=== Allocation Comparison ===")
|
Console.print("=== Allocation Comparison ===")
|
||||||
|
|
||||||
// FBIP path (rc=1): list is reused
|
|
||||||
Console.print("Test 1: FBIP path")
|
Console.print("Test 1: FBIP path")
|
||||||
let a1 = List.range(1, 50)
|
let a1 = List.range(1, 50)
|
||||||
let b1 = List.map(a1, fn(x: Int): Int => x * 2)
|
let b1 = List.map(a1, fn(x: Int): Int => x * 2)
|
||||||
let c1 = List.reverse(b1)
|
let c1 = List.reverse(b1)
|
||||||
Console.print("FBIP done")
|
Console.print("FBIP done")
|
||||||
|
|
||||||
// To show non-FBIP, we need concat which doesn't have FBIP
|
|
||||||
Console.print("Test 2: Non-FBIP path (concat)")
|
Console.print("Test 2: Non-FBIP path (concat)")
|
||||||
let x = List.range(1, 25)
|
let x = List.range(1, 25)
|
||||||
let y = List.range(26, 50)
|
let y = List.range(26, 50)
|
||||||
let z = List.concat(x, y) // concat always allocates new
|
let z = List.concat(x, y)
|
||||||
Console.print("Concat done")
|
Console.print("Concat done")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,11 @@
|
|||||||
// Demonstrating type classes (traits) in Lux
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// RGB color: rgb(255,128,0)
|
|
||||||
// Red color: red
|
|
||||||
// Green color: green
|
|
||||||
|
|
||||||
// Define a simple Printable trait
|
|
||||||
trait Printable {
|
trait Printable {
|
||||||
fn format(value: Int): String
|
fn format(value: Int): String
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Printable
|
|
||||||
impl Printable for Int {
|
impl Printable for Int {
|
||||||
fn format(value: Int): String = "Number: " + toString(value)
|
fn format(value: Int): String = "Number: " + toString(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Color type with pattern matching
|
|
||||||
type Color =
|
type Color =
|
||||||
| Red
|
| Red
|
||||||
| Green
|
| Green
|
||||||
@@ -27,15 +17,15 @@ fn colorName(c: Color): String =
|
|||||||
Red => "red",
|
Red => "red",
|
||||||
Green => "green",
|
Green => "green",
|
||||||
Blue => "blue",
|
Blue => "blue",
|
||||||
RGB(r, g, b) => "rgb(" + toString(r) + "," + toString(g) + "," + toString(b) + ")"
|
RGB(r, g, b) => "rgb(" + toString(r) + "," + toString(g) + "," + toString(b) + ")",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test
|
|
||||||
let myColor = RGB(255, 128, 0)
|
let myColor = RGB(255, 128, 0)
|
||||||
|
|
||||||
let redColor = Red
|
let redColor = Red
|
||||||
|
|
||||||
let greenColor = Green
|
let greenColor = Green
|
||||||
|
|
||||||
// Print results
|
|
||||||
fn printResults(): Unit with {Console} = {
|
fn printResults(): Unit with {Console} = {
|
||||||
Console.print("RGB color: " + colorName(myColor))
|
Console.print("RGB color: " + colorName(myColor))
|
||||||
Console.print("Red color: " + colorName(redColor))
|
Console.print("Red color: " + colorName(redColor))
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
// Demonstrating Schema Evolution in Lux
|
|
||||||
//
|
|
||||||
// Lux provides versioned types to help manage data evolution over time.
|
|
||||||
// The Schema module provides functions for creating and migrating versioned values.
|
|
||||||
//
|
|
||||||
// Expected output:
|
|
||||||
// Created user v1: Alice (age unknown)
|
|
||||||
// User version: 1
|
|
||||||
// Migrated to v2: Alice (age unknown)
|
|
||||||
// User version after migration: 2
|
|
||||||
|
|
||||||
// Create a versioned User value at v1
|
|
||||||
fn createUserV1(name: String): Unit with {Console} = {
|
fn createUserV1(name: String): Unit with {Console} = {
|
||||||
let user = Schema.versioned("User", 1, { name: name })
|
let user = Schema.versioned("User", 1, { name: name })
|
||||||
let version = Schema.getVersion(user)
|
let version = Schema.getVersion(user)
|
||||||
@@ -17,7 +5,6 @@ fn createUserV1(name: String): Unit with {Console} = {
|
|||||||
Console.print("User version: " + toString(version))
|
Console.print("User version: " + toString(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate a user to v2
|
|
||||||
fn migrateUserToV2(name: String): Unit with {Console} = {
|
fn migrateUserToV2(name: String): Unit with {Console} = {
|
||||||
let userV1 = Schema.versioned("User", 1, { name: name })
|
let userV1 = Schema.versioned("User", 1, { name: name })
|
||||||
let userV2 = Schema.migrate(userV1, 2)
|
let userV2 = Schema.migrate(userV1, 2)
|
||||||
@@ -26,7 +13,6 @@ fn migrateUserToV2(name: String): Unit with {Console} = {
|
|||||||
Console.print("User version after migration: " + toString(newVersion))
|
Console.print("User version after migration: " + toString(newVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main
|
|
||||||
fn main(): Unit with {Console} = {
|
fn main(): Unit with {Console} = {
|
||||||
createUserV1("Alice")
|
createUserV1("Alice")
|
||||||
migrateUserToV2("Alice")
|
migrateUserToV2("Alice")
|
||||||
|
|||||||
@@ -1,54 +1,30 @@
|
|||||||
// Simple Counter for Browser
|
type Model =
|
||||||
// Compile with: lux compile examples/web/counter.lux --target js -o examples/web/counter.js
|
| Counter(Int)
|
||||||
|
|
||||||
// ============================================================================
|
fn getCount(m: Model): Int =
|
||||||
// Model
|
match m {
|
||||||
// ============================================================================
|
Counter(n) => n,
|
||||||
|
}
|
||||||
type Model = | Counter(Int)
|
|
||||||
|
|
||||||
fn getCount(m: Model): Int = match m { Counter(n) => n }
|
|
||||||
|
|
||||||
fn init(): Model = Counter(0)
|
fn init(): Model = Counter(0)
|
||||||
|
|
||||||
// ============================================================================
|
type Msg =
|
||||||
// Messages
|
| Increment
|
||||||
// ============================================================================
|
| Decrement
|
||||||
|
| Reset
|
||||||
type Msg = | Increment | Decrement | Reset
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Update
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn update(model: Model, msg: Msg): Model =
|
fn update(model: Model, msg: Msg): Model =
|
||||||
match msg {
|
match msg {
|
||||||
Increment => Counter(getCount(model) + 1),
|
Increment => Counter(getCount(model) + 1),
|
||||||
Decrement => Counter(getCount(model) - 1),
|
Decrement => Counter(getCount(model) - 1),
|
||||||
Reset => Counter(0)
|
Reset => Counter(0),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// View - Returns HTML string for simplicity
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn view(model: Model): String = {
|
fn view(model: Model): String = {
|
||||||
let count = getCount(model)
|
let count = getCount(model)
|
||||||
"<div class=\"counter\">" +
|
"<div class=\"counter\">" + "<h1>Lux Counter</h1>" + "<div class=\"display\">" + toString(count) + "</div>" + "<div class=\"buttons\">" + "<button onclick=\"dispatch('Decrement')\">-</button>" + "<button onclick=\"dispatch('Reset')\">Reset</button>" + "<button onclick=\"dispatch('Increment')\">+</button>" + "</div>" + "</div>"
|
||||||
"<h1>Lux Counter</h1>" +
|
|
||||||
"<div class=\"display\">" + toString(count) + "</div>" +
|
|
||||||
"<div class=\"buttons\">" +
|
|
||||||
"<button onclick=\"dispatch('Decrement')\">-</button>" +
|
|
||||||
"<button onclick=\"dispatch('Reset')\">Reset</button>" +
|
|
||||||
"<button onclick=\"dispatch('Increment')\">+</button>" +
|
|
||||||
"</div>" +
|
|
||||||
"</div>"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Export for browser runtime
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
fn luxInit(): Model = init()
|
fn luxInit(): Model = init()
|
||||||
|
|
||||||
fn luxUpdate(model: Model, msgName: String): Model =
|
fn luxUpdate(model: Model, msgName: String): Model =
|
||||||
@@ -56,7 +32,7 @@ fn luxUpdate(model: Model, msgName: String): Model =
|
|||||||
"Increment" => update(model, Increment),
|
"Increment" => update(model, Increment),
|
||||||
"Decrement" => update(model, Decrement),
|
"Decrement" => update(model, Decrement),
|
||||||
"Reset" => update(model, Reset),
|
"Reset" => update(model, Reset),
|
||||||
_ => model
|
_ => model,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn luxView(model: Model): String = view(model)
|
fn luxView(model: Model): String = view(model)
|
||||||
|
|||||||
Reference in New Issue
Block a user