Ironwall Examples
01 Basics
This first example introduces the smallest useful Ironwall program shape: program, top-level function, typed parameters, typed literals, prefix operators, if, and the main entry point.
{program tour~one~basics@main
(function add_three ([value i5]) to i5 in
(add value $3^i5)
)
(function choose_bonus ([enabled bool]) to i5 in
(if enabled then $10^i5 else $0^i5)
)
(function main ([args <array s3>]) to i5 in
{
(var [base i5] (add_three $4^i5))
(var [total i5] (add base (choose_bonus true)))
(if (eq total $17^i5) then $0^i5 else $1^i5)
}
)
}
The outer form names the compilation unit:
{program tour~one~basics@main
Function parameters use [name type], and the return type appears after to. Integer literals carry a type suffix, for example $3^i5. Calls are prefix forms: (add value $3^i5) calls add with two arguments.
main returns 0 when the self-check succeeds. The check computes add_three(4) + choose_bonus(true), expects 17, and returns 1 if the result is wrong.
02 Control Flow
This example focuses on expression blocks, local variables, var_set, while, if, and cond.
{program tour~two~control~flow@main
(function case_score ([value i5]) to i5 in
(cond
((eq value $1^i5) $10^i5)
((eq value $2^i5) $20^i5)
((eq value $3^i5) $30^i5)
(else $99^i5)
)
)
(function sum_to ([limit i5]) to i5 in
{
(var [index i5] $1^i5)
(var [total i5] $0^i5)
(while (le index limit) in
{
(var_set total (add total index))
(var_set index (add index $1^i5))
}
)
total
}
)
(function main ([args <array s3>]) to i5 in
{
(var [total i5] (add (sum_to $5^i5) (case_score $2^i5)))
(if (eq total $35^i5) then $0^i5 else $1^i5)
}
)
}
A block is written with { ... }. It can contain declarations and statements, and its final expression becomes the block value. In sum_to, the final expression is total.
var introduces a local variable. var_set mutates it. while takes a boolean condition and a body after in.
cond is useful when several conditions return values of the same type. Here it maps 1, 2, and 3 to fixed scores, with an else fallback.
The example checks that sum_to(5) is 15, case_score(2) is 20, and the combined result is 35.
03 Classes
This example introduces classes, public properties, constructors, methods, class_new, cm_get, cm_set, and dot member access.
{program tour~three~classes@main
(class Counter
(public (property [value i5]))
(constructor ([seed i5]) in
(cm_set self value seed)
)
(public (method bump ([delta i5]) to i5 in
{
(cm_set self value (add (cm_get self value) delta))
(cm_get self value)
}
))
)
(function main ([args <array s3>]) to i5 in
{
(var [counter Counter] (class_new Counter $7^i5))
(var [first i5] (counter.bump $5^i5))
(cm_set counter value (add counter.value $1^i5))
(var [second i5] (cm_get counter value))
(if (eq (add first second) $25^i5) then $0^i5 else $1^i5)
}
)
}
class Counter defines a nominal type. The constructor runs during class_new. Inside class bodies, self refers to the current instance.
cm_set writes a class member, and cm_get reads one. Public members can also be accessed with dot syntax, as in counter.value and (counter.bump $5^i5).
The example starts a counter at 7, bumps it to 12, then sets value to 13. It succeeds when 12 + 13 is 25.
04 Arrays
This example shows the built-in raw array type <array T>.
{program tour~four~arrays@main
(function main ([args <array s3>]) to i5 in
{
(var [items <array i5>] (array_new <array i5> $4^i5 $0^i5))
(array_set items $0^i5 $3^i5)
(array_set items $1^i5 $5^i5)
(array_set items $2^i5 $7^i5)
(array_set items $3^i5 $11^i5)
(var [total i5] (add (array_get items $0^i5) (array_get items $1^i5)))
(var_set total (add total (array_get items $2^i5)))
(var_set total (add total (array_get items $3^i5)))
(var_set total (add total (array_length items)))
(if (eq total $30^i5) then $0^i5 else $1^i5)
}
)
}
<array i5> means an array whose elements are i5. array_new receives the array type, a length, and a fill value. The example creates four slots filled with 0, writes prime numbers into them, reads them back, and adds array_length.
Array indexing uses i5 indexes in these examples. The self-check expects 3 + 5 + 7 + 11 + 4 = 30.
05 Generics
This example introduces generic functions and generic classes.
{program tour~five~generics@main
(function <generic keep T> ([value T]) to T in
value
)
(class <generic Pair Left Right>
(public (property [left Left]))
(public (property [right Right]))
(constructor ([left0 Left] [right0 Right]) in
{
(cm_set self left left0)
(cm_set self right right0)
}
)
)
(function main ([args <array s3>]) to i5 in
{
(var [kept_i5 i5] (<keep i5> $7^i5))
(var [kept_bool bool] (<keep bool> true))
(var [pair <Pair i5 bool>] (class_new <Pair i5 bool> $9^i5 kept_bool))
(var [score i5] (add kept_i5 pair.left))
(if pair.right
then (if (eq score $16^i5) then $0^i5 else $1^i5)
else $2^i5
)
}
)
}
Generic declarations put type parameters in the name form:
(function <generic keep T> ...)
(class <generic Pair Left Right> ...)
Generic use sites pass concrete type arguments in the callee or class name:
(<keep i5> $7^i5)
(class_new <Pair i5 bool> $9^i5 kept_bool)
The example checks that a generic identity function preserves both i5 and bool, and that a generic Pair stores an i5 on the left and a bool on the right.
06 Unions And Match
This example focuses on union types and match.
{program tour~six~unions~match@main
(function score ([value <union i5 bool>]) to i5 in
(match value
([number i5] number)
([flag bool] (if flag then $100^i5 else $200^i5))
)
)
(function main ([args <array s3>]) to i5 in
{
(var [left i5] (score $9^i5))
(var [middle i5] (score true))
(var [right i5] (score false))
(if (eq (add left (add middle right)) $309^i5) then $0^i5 else $1^i5)
}
)
}
<union i5 bool> means the value may hold either an i5 payload or a bool payload. Passing $9^i5, true, and false to score demonstrates implicit construction of the union value from one of its member types.
match consumes the union. Each arm binds the payload with a fresh name and a concrete type:
([number i5] number)
([flag bool] ...)
The example maps 9 to 9, true to 100, and false to 200, then checks that the total is 309.
07 Closures
This example introduces function values, fn, captured locals, and function types.
{program tour~seven~closures@main
(function make_adder ([base i5]) to <to i5 from i5> in
(fn ([delta i5]) to i5 in (add base delta))
)
(function apply_twice ([value i5] [step <to i5 from i5>]) to i5 in
(step (step value))
)
(function main ([args <array s3>]) to i5 in
{
(var [bias i5] $3^i5)
(var [local_adder <to i5 from i5>] (fn ([value i5]) to i5 in (add bias value)))
(var [plus_five <to i5 from i5>] (make_adder $5^i5))
(var [result i5] (add (local_adder $4^i5) (apply_twice $1^i5 plus_five)))
(if (eq result $18^i5) then $0^i5 else $1^i5)
}
)
}
<to i5 from i5> is a function type: it returns i5 and accepts one i5 argument. fn creates a function value with the same parameter and return syntax as a named function.
Closures can capture values from their surrounding scope. make_adder captures base; local_adder captures bias.
The example computes local_adder(4) = 7 and apply_twice(1, plus_five) = 11, then checks that the total is 18.
08 Modules
This example introduces multiple compilation units, import, export, and using public definitions from another file.
Library unit:
{program tour~eight~modules~lib@defs
(export (function double ([value i5]) to i5 in
(mul value $2^i5)
))
(export (class Meter
(public (property [value i5]))
(constructor ([value0 i5]) in
(cm_set self value value0)
)
))
}
Main unit:
{program tour~eight~modules@main
(import tour~eight~modules~lib)
(function main ([args <array s3>]) to i5 in
{
(var [meter Meter] (class_new Meter (double $6^i5)))
(if (eq meter.value $12^i5) then $0^i5 else $1^i5)
}
)
}
Each .iw file starts with its own program name. The library unit exports a function and a class. The main unit imports that library:
(import tour~eight~modules~lib)
Only exported definitions are visible across units. Inside the exported class, the value property is public, so the importing module can read meter.value.
The example checks that the imported double function returns 12 for 6, then stores that result in an imported Meter class instance.
09 Recursion
This example introduces direct recursion with two familiar functions: Fibonacci and factorial.
{program tour~nine~recursion@main
(function fibonacci ([value i5]) to i5 in
(if (le value $1^i5)
then value
else (add (fibonacci (sub value $1^i5)) (fibonacci (sub value $2^i5)))
)
)
(function factorial ([value i5]) to i5 in
(if (le value $1^i5)
then $1^i5
else (mul value (factorial (sub value $1^i5)))
)
)
(function main ([args <array s3>]) to i5 in
{
(var [fib10 i5] (fibonacci $10^i5))
(var [fact6 i5] (factorial $6^i5))
(if (eq (add fib10 fact6) $775^i5) then $0^i5 else $1^i5)
}
)
}
A recursive function calls itself by name. The important rule is that each recursive path must move toward a base case.
fibonacci uses two base cases at once: any value less than or equal to 1 returns itself. Larger values are split into the two previous Fibonacci values:
(add (fibonacci (sub value $1^i5)) (fibonacci (sub value $2^i5)))
factorial uses 1 as the base result, then multiplies the current value by the factorial of the previous value:
(mul value (factorial (sub value $1^i5)))
The example checks fibonacci(10) = 55 and factorial(6) = 720. Their sum is 775, so main returns 0 when both recursive functions behave as expected.