Chapter 1 — Hello, Ingle
Here is a complete, working Ingle program. Not a fragment, not “imagine the rest” — the whole thing.
fn main() {
let name = "Ingle"
let year = 2026
println("Hello from {name}, {year}.")
}
Run it:
inglec --emit=run hello.ig
and Ingle says:
Hello from Ingle, 2026.
=> 0
Let’s take that apart, because almost everything you need for the next ten chapters is hiding in those four lines.
fn main() is where it begins. A program is a collection of functions, and execution
starts at the one called main. The fn keyword introduces a function; the empty () means
it takes no arguments. We didn’t write a return type, and that’s allowed for main — it
quietly returns 0, which is the => 0 you saw at the end.
let name = "Ingle" binds a name to a value. We didn’t say name is a string; Ingle
worked it out. More on let (and its mutable sibling var) in the next chapter.
println(...) prints a line. There’s also print, which doesn’t add the newline. Both
are built into the language — you don’t import anything to get them.
"Hello from {name}, {year}." is an interpolated string. Anything inside { } is an
expression that gets computed and slotted into the text. Every string in Ingle can interpolate;
there’s no special prefix to opt in.
A function with a job
main calling println is fine, but functions earn their keep by taking input and returning
output. Here’s one that adds two integers:
fn add(a: int, b: int) -> int {
return a + b
}
fn main() -> int {
return add(20, 22)
}
=> 42
Two new things. First, function parameters must have types — a: int, b: int. Ingle will
happily infer the type of a local let, but at the boundary of a function it insists you
spell things out. That boundary is a contract between the caller and the callee, and it’s
exactly where being explicit pays off (for you, for the next person, and — as we’ll see — for
the machine). Second, -> int declares the return type. Here main returns an int too,
so its result 42 shows up as => 42.
Comments, and the comment that becomes documentation
Ordinary comments run from // to the end of the line. There are no block comments; Ingle
doesn’t have them, and after a while you won’t miss them.
// This is a comment. It explains the line below.
let tau = 6 // comments can sit at the end of a line, too
But three slashes — /// — make a doc comment, and those are special. Placed on the
line(s) just above a declaration, they document it, and Ingle can render them two ways from
the one source: the editor shows them when you hover, and inglec --emit=docs turns them into
a Markdown page. Write the explanation once; get the tooltip and the manual for free.
/// A point on the plane.
/// Copied by value.
struct Point {
/// The horizontal coordinate.
x: int
y: int
}
Four or more slashes (////) are just an ordinary comment again. Ingle reserves exactly three
for documentation, which is a pleasingly specific decision.
No semicolons
You may have noticed the lack of semicolons. This is not an oversight. Ingle has no
statement terminator; a newline ends a statement — but only when the line could sensibly
end there. If a line ends on something that obviously wants more (a +, a comma, an open
bracket, an =), Ingle knows you’re mid-thought and reads on:
let total = 1 + // the trailing '+' means "more coming" — the line continues
2 +
3
The rule of thumb that will keep you out of trouble: break after an operator, never before
it. Put the + at the end of the line, not the start of the next one. Do that and you’ll
never think about this again.
Fireside trivia. Languages disagree fiercely about semicolons. C demands them. Python banned them. Go made them optional by having the lexer secretly insert them at line breaks — and Ingle does essentially the same trick, emitting an invisible terminator only after a token that can legally end a statement. So the semicolons are still there, in a sense. They’ve just gone into hiding and taken your shift-key’s workload with them.