Skip to main content

AILANG Examples

Learn AILANG through interactive examples. Each example demonstrates a key feature with real working code from the repository.

Quick Start Examples

Hello World

The simplest AILANG program with I/O effects:

examples/runnable/hello.ail
-- hello.ail - Simple hello world program
module examples/runnable/hello

import std/io (print)

-- Entry module with exported main function
export func main() -> () ! {IO} =
print("Hello, AILANG!")
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/hello.ail

Key concepts:

  • ! {IO} declares the function performs I/O
  • println is imported from std/io
  • --caps IO grants I/O capability at runtime

Recursion & Pure Functions

AILANG uses recursion instead of loops for deterministic control flow:

examples/runnable/recursion_factorial.ail
-- recursion_factorial.ail
-- Demonstrates simple recursive function with LetRec
-- Status:Working (M-R4 recursion support)

module examples/runnable/recursion_factorial

import std/io (println)

-- Classic factorial using recursion
pure func factorial(n: int) -> int
tests [
(0, 1),
(1, 1),
(5, 120),
(10, 3628800),
(12, 479001600)
]
{
if n <= 1 then 1 else n * factorial(n - 1)
}

-- Tail-recursive factorial with accumulator
pure func factorialTail(n: int, acc: int) -> int
tests [
((0, 1), 1),
((1, 1), 1),
((5, 1), 120),
((10, 1), 3628800),
((12, 1), 479001600)
]
{
if n <= 1 then acc else factorialTail(n - 1, n * acc)
}

export func main() -> () ! {IO} {
let result1 = factorial(5);
let result2 = factorialTail(10, 1);

println("factorial(5) = " ++ show(result1));
println("factorialTail(10, 1) = " ++ show(result2))
}
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/recursion_factorial.ail

Note: The main function is the default entrypoint. This example prints factorial results using IO.

Key concepts:

  • factorial is pure (no effects declared)
  • Recursion replaces loops for deterministic reasoning
  • show() converts int to string for printing

Pattern Matching

Lists & Destructuring

Pattern matching is AILANG's primary control flow mechanism:

examples/runnable/list_sum.ail
-- list_sum.ail - List operations with pattern matching
-- Demonstrates ADT destructuring and recursion

type IntList = INil | ICons(int, IntList)

-- Sum all integers in a list using letrec for recursion
letrec sum = \list.
match list {
INil => 0,
ICons(head, tail) => head + sum(tail)
}
in
letrec length = \list.
match list {
INil => 0,
ICons(_, tail) => 1 + length(tail)
}
in
let numbers = ICons(1, ICons(2, ICons(3, ICons(4, ICons(5, INil))))) in
(sum(numbers), length(numbers))
View source on GitHub

Run it:

ailang run examples/runnable/list_sum.ail

Output:

(15, 5)

Key concepts:

  • INil matches empty list
  • ICons(head, tail) destructures list
  • letrec enables recursive function definitions
  • Exhaustive matching required (compiler enforces)

Algebraic Data Types (ADTs)

Define custom types with multiple constructors:

examples/runnable/adt_tree.ail
-- adt_tree.ail - Binary tree ADT with sum operation
-- Demonstrates recursive ADTs and pattern matching
module examples/runnable/adt_tree

type Tree =
| Leaf(int)
| Node(Tree, int, Tree)

-- Sum all values in a tree (recursive pattern matching)
pure func sumTree(tree: Tree) -> int =
match tree {
Leaf(n) => n,
Node(left, value, right) =>
sumTree(left) + value + sumTree(right)
}

-- Count nodes in tree
pure func countNodes(tree: Tree) -> int =
match tree {
Leaf(_) => 1,
Node(left, _, right) => 1 + countNodes(left) + countNodes(right)
}

export func main() -> () ! {IO} =
let tree = Node(Leaf(1), 2, Node(Leaf(3), 4, Leaf(5))) in
let _ = println("Tree sum: " ++ show(sumTree(tree))) in
println("Node count: " ++ show(countNodes(tree)))
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/adt_tree.ail

Output:

Tree sum: 15
Node count: 5

Key concepts:

  • type Tree = defines an ADT with 2 constructors
  • pure func declares functions without side effects
  • Pattern matching handles all cases
  • Compiler ensures exhaustiveness

Effect System

Multiple Effects

Functions can declare multiple effects they perform:

examples/runnable/effects_fs_io.ail
-- effects_fs_io.ail - Demonstrates multiple effect capabilities
-- Shows combining IO and FS effects in a single function
module examples/runnable/effects_fs_io

import std/io (println)
import std/fs (fileExists, readFile, writeFile)

-- Function that combines IO and FS effects
-- Note: ! {IO, FS} declares both capabilities
export func main() -> () ! {IO, FS} {
let path = "/tmp/ailang_test.txt";
let content = "Hello from AILANG!";

-- Write a test file (FS effect)
writeFile(path, content);

-- Print confirmation (IO effect)
println("Wrote: " ++ content);

-- Check if file exists (FS effect)
let fileOk = fileExists(path);
println("File exists: " ++ show(fileOk));

-- Read it back (FS effect)
let readBack = readFile(path);
println("Read back: " ++ readBack)
}
View source on GitHub

Run it:

ailang run --caps IO,FS examples/runnable/effects_fs_io.ail

Key concepts:

  • ! {IO, FS} declares both I/O and filesystem effects
  • Sequential statements use ; in block expressions
  • Import functions from stdlib modules

Effect Polymorphism

Functions can be generic over effects. This allows writing reusable code that works with any combination of capabilities:

-- Effect row polymorphism: {IO | e} means "IO plus any other effects"
-- This allows functions to compose while preserving effect safety

Key concepts:

  • Effect rows can be extended with {IO | e} syntax
  • Effect polymorphism enables reusable library functions
  • Effects compose naturally when calling functions

Type System

Type Inference

AILANG infers types automatically using Hindley-Milner:

examples/runnable/type_inference.ail
-- Showcase: Type Inference
-- The type checker automatically infers types!
module examples/runnable/type_inference

export func main() -> () ! {IO} =
let double = \x. x * 2 in
let result = double(21) in
println("Type inference: " ++ show(result))
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/type_inference.ail

Key concepts:

  • Type inference works across function boundaries
  • Explicit annotations optional but recommended
  • :type command in REPL shows inferred types

Records & Subsumption

Records are structural types with width subtyping:

examples/runnable/records.ail
-- records.ail - Record examples
-- Demonstrates record literals, field access, and nested records

-- =======================
-- BASIC RECORDS
-- =======================

-- Simple record literal
{name: "Alice", age: 30}
-- Output: {name: "Alice", age: 30}

-- Record with multiple types
{id: 1, name: "Bob", active: true, score: 98.5}
-- Output: {id: 1, name: "Bob", active: true, score: 98.5}

-- =======================
-- FIELD ACCESS
-- =======================

-- Single field access
let person = {name: "Charlie", age: 25} in
person.name
-- Output: "Charlie"

-- Multiple field access
let user = {id: 123, email: "test@example.com"} in
show(user.id) ++ " - " ++ user.email
-- Output: "123 - test@example.com"

-- =======================
-- NESTED RECORDS
-- =======================

-- Record with nested structure
let company = {
name: "Acme Corp",
ceo: {
name: "Jane Doe",
age: 45
}
} in
company.ceo.name
-- Output: "Jane Doe"

-- Deeper nesting
let data = {
user: {
profile: {
name: "Deep",
email: "deep@test.com"
}
}
} in
data.user.profile.email
-- Output: "deep@test.com"

-- =======================
-- RECORDS IN EXPRESSIONS
-- =======================

-- Record with computed fields
let x = 10 in
let y = 20 in
{sum: x + y, product: x * y, x: x, y: y}
-- Output: {sum: 30, product: 200, x: 10, y: 20}

-- Using record fields in calculations
let rect = {width: 5, height: 10} in
rect.width * rect.height
-- Output: 50

-- =======================
-- RECORDS WITH FUNCTIONS
-- =======================

-- Record containing lambda functions
let math = {
add: \x y. x + y,
multiply: \x y. x * y,
square: \x. x * x
} in
math.square(5)
-- Output: 25

-- Using functions from records
let ops = {
double: \x. x * 2,
triple: \x. x * 3
} in
ops.double(21)
-- Output: 42

-- =======================
-- PATTERN MATCHING
-- =======================

-- Record patterns (v0.6.2+)
-- See examples/runnable/record_patterns.ail for comprehensive examples
let person = {name: "Alice", age: 30} in
match person {
{name, age} => name ++ " is " ++ show(age)
}
-- Output: "Alice is 30"

-- =======================
-- COMPLEX EXAMPLES
-- =======================

-- Record with mixed types and nesting
let config = {
app: {
name: "MyApp",
version: "1.0.0"
},
server: {
host: "localhost",
port: 8080,
ssl: false
},
features: {
debug: true,
logging: true
}
} in
config.app.name ++ " v" ++ config.app.version
-- Output: "MyApp v1.0.0"

-- Building a result from record fields
let point = {x: 3, y: 4} in
let distance = \p.
let dx = p.x * p.x in
let dy = p.y * p.y in
dx + dy -- Would be sqrt(dx + dy) with math functions
in
distance(point)
-- Output: 25
View source on GitHub

Run it:

ailang run examples/runnable/records.ail

Key concepts:

  • Records support structural typing
  • Width subtyping: {a, b, c}{a, b}
  • Field access: record.field
  • Nested records and lambda fields supported

JSON & HTTP

JSON Parsing

Parse and work with JSON data:

examples/runnable/json_parsing.ail
-- json_parsing.ail - JSON parsing example
-- Demonstrates JSON decode with convenience functions (v0.6.4+)
module examples/runnable/json_parsing

import std/io (println)
import std/json (decode, getString, getNumber)
import std/result (Result, Ok, Err)
import std/option (Option, Some, None)

-- Parse JSON and extract values using convenience functions
export func main() -> () ! {IO} {
let jsonStr = "{\"name\": \"AILANG\", \"version\": 0.6}";

match decode(jsonStr) {
Ok(obj) => {
println("Parsed JSON successfully!");

-- Extract name field using getString (combines get + asString)
match getString(obj, "name") {
Some(name) => println("Name: " ++ name),
None => println("Name not found or not a string")
};

-- Extract version using getNumber
match getNumber(obj, "version") {
Some(ver) => println("Version: " ++ show(ver)),
None => println("Version not found or not a number")
}
},
Err(e) => println("Parse error: " ++ e)
}
}
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/json_parsing.ail

Key concepts:

  • decode returns Result[Json, string]
  • Pattern match on JSON structure using Ok/Err
  • Use get and asString helpers for field access

HTTP Requests

Make HTTP requests with the Net effect:

examples/runnable/http_simple.ail
-- http_simple.ail - Simple HTTP request example
-- Demonstrates Net effect for HTTP calls
module examples/runnable/http_simple

import std/io (println)
import std/net (httpGet)
import std/string (length)

-- Fetch a URL and print status
export func main() -> () ! {IO, Net} {
let url = "https://httpbin.org/get";

-- Simple GET request (returns body string)
println("Fetching: " ++ url);

let response = httpGet(url);
println("Response length: " ++ show(length(response)) ++ " bytes")
}
View source on GitHub

Run it:

ailang run --caps IO,Net examples/runnable/http_simple.ail

Key concepts:

  • ! {IO, Net} declares network capability
  • httpGet returns response body as string
  • DNS rebinding protection built-in

Advanced Examples

Higher-Order Functions

Functions are first-class values:

examples/runnable/lambdas_higher_order.ail
-- Showcase: Higher-Order Functions
-- This example demonstrates functions that take or return functions.
module examples/runnable/lambdas_higher_order

export func main() -> () ! {IO} =
let _ = println("=== Higher-Order Functions ===") in

-- Function composition
let compose = \f. \g. \x. f(g(x)) in
let double = \x. x * 2 in
let addOne = \x. x + 1 in
let doubleThenAddOne = compose(addOne)(double) in
let _ = println("Function composition:") in
let _ = println(" doubleThenAddOne(5) = " ++ show(doubleThenAddOne(5))) in
let _ = println(" (first double, then add one)") in

-- Map-like function over a pair
let applyToBoth = \f. \x. \y. [f(x), f(y)] in
let square = \x. x * x in
let result = applyToBoth(square)(3)(4) in
let _ = println("") in
let _ = println("Apply function to both values:") in
let _ = println(" applyToBoth(square)(3)(4) = " ++ show(result)) in

-- Function that returns a function
let makeOperation = \op. \x. \y. op(x)(y) in
let add = \x. \y. x + y in
let multiply = \x. \y. x * y in
let adder = makeOperation(add) in
let multiplier = makeOperation(multiply) in
let _ = println("") in
let _ = println("Function returning function:") in
let _ = println(" adder(5)(3) = " ++ show(adder(5)(3))) in
let _ = println(" multiplier(5)(3) = " ++ show(multiplier(5)(3))) in

-- Applying a function twice
let twice = \f. \x. f(f(x)) in
let increment = \x. x + 1 in
let quadruple = twice(double) in
let _ = println("") in
let _ = println("Applying function twice:") in
let _ = println(" twice(increment)(5) = " ++ show(twice(increment)(5))) in
println(" quadruple(5) = " ++ show(quadruple(5)))
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/lambdas_higher_order.ail

Key concepts:

  • \x. x * 2 is a lambda function
  • Functions can be passed as arguments
  • Function composition: \f. \g. \x. f(g(x))
  • Closures capture environment

Mutual Recursion

Multiple functions can call each other:

examples/runnable/recursion_mutual.ail
-- recursion_mutual.ail
-- Demonstrates mutually recursive functions
-- Status:Working (M-R4 recursion support)

module examples/runnable/recursion_mutual

import std/io (println)

-- Mutually recursive functions to test even/odd
-- Now supported! M-TESTING-DEPS enables testing mutually recursive functions
export pure func isEven(n: int) -> bool
tests [
(0, true),
(1, false),
(2, true),
(10, true),
(17, false)
]
{
if n == 0 then true else isOdd(n - 1)
}

export pure func isOdd(n: int) -> bool
tests [
(0, false),
(1, true),
(2, false),
(10, false),
(17, true)
]
{
if n == 0 then false else isEven(n - 1)
}

export func main() -> () ! {IO} {
let even42 = isEven(42);
let odd42 = isOdd(42);
let even17 = isEven(17);
let odd17 = isOdd(17);

println("isEven(42) = " ++ show(even42));
println("isOdd(42) = " ++ show(odd42));
println("isEven(17) = " ++ show(even17));
println("isOdd(17) = " ++ show(odd17))
}
View source on GitHub

Run it:

ailang run --caps IO examples/runnable/recursion_mutual.ail

Key concepts:

  • pure func declares functions without side effects
  • Functions can reference each other (mutual recursion)
  • Built-in tests verify correctness: tests [(0, true), (1, false), ...]

Try It Yourself

In the Playground

Visit the Playground to run code interactively in your browser.

With CLI

Install AILANG and run examples locally:

# Install (macOS Apple Silicon)
curl -L https://github.com/sunholo-data/ailang/releases/latest/download/ailang-darwin-arm64.tar.gz | tar -xz
sudo mv ailang /usr/local/bin/

# Or build from source
git clone https://github.com/sunholo-data/ailang && cd ailang && make install

# Run an example
ailang run --caps IO examples/runnable/hello.ail

# Interactive REPL
ailang repl

From GitHub

Clone the repository for all 98+ examples:

git clone https://github.com/sunholo-data/ailang
cd ailang

# Run specific examples
ailang run --caps IO examples/runnable/recursion_factorial.ail

Implementation Status

This section tracks AILANG's implementation progress and is automatically updated by CI/CD.

The example verification status shows what percentage of AILANG features are fully implemented and working. Each example exercises specific language features, so the pass rate directly reflects implementation completeness.

Status

Examples

Example Verification Status

Last updated: 2026-01-30 (Auto-updated by CI)

Summary: 107 passed, 3 failed, 0 skipped (Total: 110)

Overall: 107/110 examples working (97%)

Example FileStatusNotes
runnable/adt_list_fields.ail✅ Pass
runnable/adt_option.ail✅ Pass
runnable/adt_simple.ail✅ Pass
runnable/adt_tree.ail✅ Pass
runnable/ai_call.ail✅ Pass
runnable/ai_effect.ail✅ Pass
runnable/arithmetic.ail✅ Pass
runnable/array_adt.ail✅ Pass
runnable/array_basic.ail✅ Pass
runnable/array_grid.ail✅ Pass
runnable/block_demo.ail✅ Pass
runnable/block_recursion.ail✅ Pass
runnable/bug_float_comparison.ail✅ Pass
runnable/claude_haiku_call.ail✅ Pass
runnable/cli_args_demo.ail✅ Pass
runnable/closures.ail✅ Pass
runnable/complex_types.ail✅ Pass
runnable/contracts/basic.ail✅ Pass
runnable/contracts/park.ail✅ Pass
runnable/conway_grid.ail✅ Pass
runnable/debug_effect.ail✅ Pass
runnable/debug_types_demo.ail✅ Pass
runnable/demo_ai_api.ail✅ Pass
runnable/demos/adt_pipeline.ail✅ Pass
runnable/demos/hello_io.ail✅ Pass
runnable/effect_budgets.ail✅ Pass
runnable/effect_budgets_exhausted.ail❌ FailError: execution failed: effect 'IO' budget exhauste...
runnable/effect_budgets_multi.ail❌ FailError: execution failed: effect 'Clock' budget exhau...
runnable/effect_budgets_rand.ail❌ FailError: execution failed: effect 'Rand' budget exhaus...
runnable/effects_basic.ail✅ Pass
runnable/effects_fs_io.ail✅ Pass
runnable/effects_pure.ail✅ Pass
runnable/factorial.ail✅ Pass
runnable/func_expressions.ail✅ Pass
runnable/game_npc_dialogue.ail✅ Pass
runnable/guards_basic.ail✅ Pass
runnable/guards_module.ail✅ Pass
runnable/hello.ail✅ Pass
runnable/http_simple.ail✅ Pass
runnable/if_else_chain.ail✅ Pass
runnable/imported_adt_types.ail✅ Pass
runnable/imports.ail✅ Pass
runnable/imports_basic.ail✅ Pass
runnable/json_array_extraction.ail✅ Pass
runnable/json_basic_decode.ail✅ Pass
runnable/json_helpers.ail✅ Pass
runnable/json_parsing.ail✅ Pass
runnable/lambdas.ail✅ Pass
runnable/lambdas_advanced.ail✅ Pass
runnable/lambdas_closures.ail✅ Pass
runnable/lambdas_curried.ail✅ Pass
runnable/lambdas_higher_order.ail✅ Pass
runnable/letrec_recursion.ail✅ Pass
runnable/list_extremes.ail✅ Pass
runnable/list_helpers.ail✅ Pass
runnable/list_pattern_cons.ail✅ Pass
runnable/list_pattern_records.ail✅ Pass
runnable/list_patterns.ail✅ Pass
runnable/list_sum.ail✅ Pass
runnable/match_arm_block.ail✅ Pass
runnable/match_arm_block_io.ail✅ Pass
runnable/match_in_block.ail✅ Pass
runnable/math_trig.ail✅ Pass
runnable/micro_block_if.ail✅ Pass
runnable/micro_block_seq.ail✅ Pass
runnable/micro_io_echo.ail✅ Pass
runnable/micro_option_map.ail✅ Pass
runnable/micro_record_person.ail✅ Pass
runnable/nested_match_minimal.ail✅ Pass
runnable/nested_match_simple.ail✅ Pass
runnable/no_loops_filter_map.ail✅ Pass
runnable/no_loops_fold.ail✅ Pass
runnable/no_loops_recursion.ail✅ Pass
runnable/option_demo.ail✅ Pass
runnable/pattern_sugar.ail✅ Pass
runnable/patterns.ail✅ Pass
runnable/poly_adt_result_type.ail✅ Pass
runnable/polymorphic_adt.ail✅ Pass
runnable/polymorphic_comparison_simple.ail✅ Pass
runnable/polymorphic_lambdas_phase1.ail✅ Pass
runnable/record_in_match.ail✅ Pass
runnable/record_patterns.ail✅ Pass
runnable/record_update.ail✅ Pass
runnable/record_width_subtyping.ail✅ Pass
runnable/records.ail✅ Pass
runnable/recursion_factorial.ail✅ Pass
runnable/recursion_fibonacci.ail✅ Pass
runnable/recursion_match.ail✅ Pass
runnable/recursion_mutual.ail✅ Pass
runnable/recursion_quicksort.ail✅ Pass
runnable/simple.ail✅ Pass
runnable/simple_func_match.ail✅ Pass
runnable/stdlib_demo.ail✅ Pass
runnable/stdlib_demo_simple.ail✅ Pass
runnable/string_chars.ail✅ Pass
runnable/string_contains.ail✅ Pass
runnable/string_parsing.ail✅ Pass
runnable/string_repeat.ail✅ Pass
runnable/string_split.ail✅ Pass
runnable/test_cli_io.ail✅ Pass
runnable/test_fizzbuzz.ail✅ Pass
runnable/test_guard_bool.ail✅ Pass
runnable/test_import_func.ail✅ Pass
runnable/test_io_builtins.ail✅ Pass
runnable/test_module_minimal.ail✅ Pass
runnable/type_classes.ail✅ Pass
runnable/type_inference.ail✅ Pass
runnable/type_inference_regression.ail✅ Pass
runnable/typeclasses.ail✅ Pass
runnable/values_basic.ail✅ Pass

Known Limitations

Some examples may fail due to features still under development. See the maintained LIMITATIONS.md for the current list of known issues and workarounds.


Contributing Examples

Found a bug? Have a cool example to share?


Next Steps

See Also