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 typesa: 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.