Learn Hilde
This tour shows complete programmes from the examples/ directory of the Hilde repository, each with what it printed when it was built and run for this page. For the rules behind them, read the basics; for every error code, the error codes.
Hello, world
File: HelloWorld.hih
module Example.HelloWorld is
"""
No contract: the exit code of the programme, the result of what it did.
"""
function main(): ExitCode;
end module.
File: HelloWorld.hiq
module body Example.HelloWorld is
function main(): ExitCode is
printLn("Hello, world!");
return ExitSuccess();
end;
end module body.
Output of ./main (exit status 0):
Hello, world!
examples/hello-world in the Hilde repository, hilde 1.0.0Records with an invariant
Interval is a record with an invariant, checked whenever a value leaves its module. makeInterval answers with an Option: an interval whose low end is above its high end is refused with None, not with a crash.
File: Interval.hih
"""
A closed interval of whole numbers whose low end is never above its high
end. The type is opaque, so only this module builds and changes one, and
its invariant is checked whenever a value leaves the module.
"""
module Example.Interval is
"""
An interval from low to high, both included.
"""
type Interval: Linear;
"""
The interval from low to high, or None when low is above high.
No contract: an Option result, which an ensures cannot look into.
"""
function makeInterval(low: Nat32, high: Nat32): Option[Interval];
"""
How many numbers the interval holds: high minus low plus one.
No contract: a number read from its argument; its type gives its range, and nothing cheaper holds.
"""
generic [R: Region]
function width(interval: &[Interval, R]): Nat32;
"""
Moves the high end up by amount. Returns false and leaves the interval
as it was when the high end would pass the largest Nat32.
No contract: the answer is a Bool, nothing cheaper holds.
"""
generic [R: Region]
function stretch(interval: &![Interval, R], amount: Nat32): Bool;
"""
The high end of the interval.
No contract: a number read from its argument; its type gives its range, and nothing cheaper holds.
"""
generic [R: Region]
function highEnd(interval: &[Interval, R]): Nat32;
"""
Ends the interval.
No contract: it only frees or closes what it is given.
"""
function destroyInterval(interval: Interval): Unit;
end module.
File: LearnRecords.hiq
import Example.Interval (
Interval,
makeInterval,
width,
stretch,
highEnd,
destroyInterval
);
module body Example.LearnRecords is
record Day: Linear is
morning: Interval;
evening: Interval;
end;
-- A write reference to the evening alone, from a write reference to
-- the whole day. It uses up the reference to the day.
generic [R: Region]
function stretchEvening(day: &![Day, R], amount: Nat32): Bool is
return stretch(&!(day->evening), amount);
end;
generic [R: Region]
function hours(day: &[Day, R]): Nat32 is
return width(&(day->morning)) + width(&(day->evening));
end;
function makeDay(morning: Interval, evening: Interval): Day is
return Day(morning => morning, evening => evening);
end;
function main(): ExitCode is
case makeInterval(9, 5) of
when Some(value as wrong: Interval) do
printLn("an interval from 9 to 5, which must not be");
destroyInterval(wrong);
when None do
printLn("an interval from 9 to 5: refused, the low end is above the high end");
end case;
case makeInterval(8, 11) of
when Some(value as morning: Interval) do
case makeInterval(17, 19) of
when Some(value as evening: Interval) do
var day: Day := makeDay(morning, evening);
print("hours in the day: ");
printLn(hours(&day));
print("the evening stretched by two: ");
printLn(stretchEvening(&!day, 2));
print("stretched past the largest number: ");
printLn(stretchEvening(&!day, maximum_nat32));
borrow view: &[Day, V] := &day do
print("the evening ends at: ");
printLn(highEnd(&(view->evening)));
print("hours now: ");
printLn(hours(view));
end borrow;
let { morning as m: Interval, evening as e: Interval } := day;
destroyInterval(m);
destroyInterval(e);
when None do
printLn("no evening");
destroyInterval(morning);
end case;
when None do
printLn("no morning");
end case;
return ExitSuccess();
end;
end module body.
Output of ./main (exit status 0):
an interval from 9 to 5: refused, the low end is above the high end hours in the day: 7 the evening stretched by two: true stretched past the largest number: false the evening ends at: 21 hours now: 9
examples/learn-records in the Hilde repository, hilde 1.0.0Templates
A template in Jinja2 syntax beside the module. The compiler checks it against the record it is given and translates it into Hilde code; nothing is parsed at run time.
File: order.jinja
Order for {{ customer|upper }}
{%- for line in lines %}
{{ loop.index }}. {{ line.name }}, {{ line.count }} of them{% if loop.last %} (the last line){% endif %}
{%- endfor %}
{% if rush %}Ship today.{% else %}Ship this week.{% endif %}
File: LearnTemplate.hiq
import Standard.Buffer (
Buffer,
allocateEmpty,
insertBack,
removeLast,
destroyEmpty,
length
);
import Standard.String (
String,
fromLiteral,
destroyString,
length as textLength,
getSpan
);
import Standard.StringBuilder (
StringBuilder,
makeStringBuilder,
finalize
);
import Standard.Template (
Render
);
module body Example.LearnTemplate is
record Line: Linear is
name: String;
count: Int32;
end;
record Order: Linear is
customer: String;
lines: Buffer[Line];
rush: Bool;
end;
-- The template order.jinja, in the syntax of Jinja2, is read by the
-- compiler and turned into code here: a name in the template that the
-- record does not have, or a filter that does not exist, is a compile
-- error, not a surprise at run time.
generic [R: Region]
function render(order: &[Order, R]): String is
var out: StringBuilder := makeStringBuilder();
@template("order.jinja", order, out);
return finalize(out);
end;
function destroyLines(lines: Buffer[Line]): Unit is
var rest: Buffer[Line] := lines;
while length(&rest) > 0 do
let line: Line := removeLast(&!rest);
let { name: String, count: Int32 } := line;
destroyString(name);
end while;
destroyEmpty(rest);
return nil;
end;
function main(): ExitCode is
var lines: Buffer[Line] := allocateEmpty();
insertBack(&!lines, Line(name => fromLiteral("apples"), count => 6));
insertBack(&!lines, Line(name => fromLiteral("pears"), count => 2));
insertBack(&!lines, Line(name => fromLiteral("plums"), count => 12));
let order: Order := Order(
customer => fromLiteral("Ann"),
lines => lines,
rush => true
);
let text: String := render(&order);
printLn(getSpan(&text, 0, textLength(&text) - 1));
destroyString(text);
let { customer: String, lines as rest: Buffer[Line], rush: Bool } := order;
destroyString(customer);
destroyLines(rest);
return ExitSuccess();
end;
end module body.
Output of ./main (exit status 0):
Order for ANN 1. apples, 6 of them 2. pears, 2 of them 3. plums, 12 of them (the last line) Ship today.
examples/learn-template in the Hilde repository, hilde 1.0.0A graph
Standard.Graph is a directed graph with values on nodes and edges. The example finds the way with the fewest roads and the one with the fewest kilometres through a small map (source in examples/learn-graph).
Output of ./main (exit status 0):
fewest roads: Aachen Cologne Frankfurt fewest kilometres: Aachen Bonn Frankfurt, 265 km
examples/learn-graph in the Hilde repository, hilde 1.0.0Tasks
Four tasks in a scope and a fifth with an arena of its own; the output is the same on every run (source in examples/learn-tasks).
Output of ./main (exit status 0):
5239625 36552125 99114625 192927125 333833500 338350
examples/learn-tasks in the Hilde repository, hilde 1.0.0More examples
The repository has sixty example directories; the learn-* directories each show one feature or one module: regular expressions, the sandbox, SQLite and SQLCipher, passwords, sessions, a FastCGI server, files, time, archives and more. Each has a README.md that says how to build it and what comes out. The guide Learning Hilde leads from the first programme on, and the traps lists the traps met in writing Hilde, with the wrong and the right code.
Playground
A playground in the browser is planned on top of Hilde's WebAssembly target: a module for wasm32-wasi is built with zig cc, and the repository has examples for a browser and for the command line (examples/wasm-browser, examples/wasm-cli). There is no playground on this site yet, and the site works without JavaScript.