Finch/docs/en/for-beginners.md
Claude 0bf66740df
Compiler and docs on every system: macOS Intel in CI, releases made by CI, docs for vectors and --cpu
- CI: macOS on Apple Silicon and Intel (macos-15, macos-15-intel); a tag v* attaches every package
  (Linux x64/ARM64, macOS ARM64/x64, Windows x64, the VS Code extension) to its release
- vectors: fma is llvm.fmuladd (llvm.fma was a libm call per lane without FMA hardware: 3.5x slower)
- examples/simd.fch: a dot product eight at a time, timed against the plain loop
- README and the English guides: installing on Linux, macOS and Windows; vectors (SIMD); --cpu;
  -g is DWARF on Windows too (lldb from llvm-mingw); no more mention of the C++ compiler
- comments left from the C++ compiler (src/target.cpp, src/codegen.cpp, "never frees") brought up to date

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Psc42moPLwP43Cmb9oZqSR
2026-11-01 13:57:28 +00:00

26 KiB
Raw Blame History

Finch for Beginners

Never written a program before? This guide is for you. No experience needed, and no math beyond what you learned in primary school. Take it slowly, one section at a time, and type every example yourself: that is how it sticks.

🇵🇱 Polska wersja: dla-poczatkujacych.md


Contents

  1. What is a program?
  2. Getting Finch ready
  3. Your first program
  4. Showing things on the screen
  5. Boxes with names: variables
  6. Kinds of values
  7. Doing math
  8. Making decisions: if
  9. Repeating things: loops
  10. Your own commands: functions
  11. Lists: many values in one box
  12. Playing with text
  13. Asking the user
  14. Your own kinds of values: structs
  15. Saving to a file
  16. When something goes wrong
  17. Small projects to try
  18. Cheat sheet
  19. Words you will hear

1. What is a program?

A computer is very fast, but it does not think. It only does exactly what it is told, one step at a time.

A program is a list of those steps, written down. Like a recipe:

  1. Take two eggs.
  2. Break them into a bowl.
  3. Stir.

A programming language is the way we write that recipe so the computer understands it. Finch is one of those languages. It was made to be small and simple, so you can learn it quickly.

The computer reads your program from top to bottom, line by line.


2. Getting Finch ready

Finch runs on Linux, macOS and Windows computers. If a teacher, parent or friend has already set it up for you, skip to the next section. (On a Mac or on Windows, ask someone to follow the guide for technicians: it has a ready-made Finch for both.)

Otherwise, open the Terminal program (a window where you type commands) and type these lines one by one, pressing Enter after each:

git clone https://github.com/Nexoniarz/Finch.git
cd Finch
nix-shell
bootstrap/build.sh

If the last line finishes without the word error, Finch is ready. (If your computer does not have nix-shell, ask someone to follow the guide for technicians. It explains the other way.)

To check it works, type:

./build/finch version

You should see something like Finch 2.6.0.


3. Your first program

  1. Open any text editor (for example Text Editor, Kate, gedit or VS Code).
  2. Type exactly this:
fn main() {
    print("Hello!")
}
  1. Save it in the Finch folder as hello.fch. The .fch at the end tells everyone it is a Finch program.
  2. In the Terminal, type:
./build/finch run hello.fch

You will see:

Hello!

🎉 You have just written and run a program.

What did we write?

fn main() {            ← "here starts the main part of my program"
    print("Hello!")    ← "show the text Hello! on the screen"
}                      ← "the main part ends here"
  • fn main() is where every Finch program starts. Each program has exactly one main.
  • The curly brackets { and } hold everything that belongs to main, like the two covers of a book.
  • print(...) shows something on the screen.
  • Text always goes inside double quotes: "Hello!".

The spaces at the start of print are not required, but they make it easy to see what is inside the brackets. Always add them. It is a good habit.


4. Showing things on the screen

print can show text and numbers. Put a comma between things, and Finch adds a space for you:

fn main() {
    print("I am", 10, "years old")
    print("2 + 2 =", 2 + 2)
    print()
    print("The line above is empty")
}

Shows:

I am 10 years old
2 + 2 = 4

The line above is empty

Every print starts a new line. print() with nothing inside makes an empty line.

Notes for yourself: comments

Anything after // is ignored by the computer. Use it to leave notes:

// This program says hi
fn main() {
    print("Hi")   // this shows Hi
}

5. Boxes with names: variables

Imagine a box with a label on it. You can put something in the box and later look at what is inside by reading the label. In programming that box is called a variable.

fn main() {
    age := 10
    print(age)
}

age := 10 means: "make a new box, label it age, and put 10 in it."

You can change what is in the box later, using just =:

fn main() {
    score := 0
    print("Score:", score)

    score = 5
    print("Score:", score)

    score = score + 1
    print("Score:", score)
}
Score: 0
Score: 5
Score: 6

score = score + 1 reads as: "take what is in score, add 1, and put the result back into score." There is a shorter way to write the same thing: score += 1.

Remember:

  • := makes a new box. You use it only once per name.
  • = puts a new value into a box that already exists.

Naming your boxes

Names can use the English letters a–z and A–Z, digits and _, but cannot start with a digit: age, total_points, player2 are fine. 2player is not. Letters like é, ü or ż cannot be used in names, only inside text in quotes: print("Zürich") is fine. Pick names that say what is inside: price is better than p.


6. Kinds of values

Finch keeps track of what kind of thing is in each box. The kind is called a type. These are the ones you will use most:

Type What it holds Examples
int whole numbers 0, 42, -7
float numbers with a decimal point 3.14, 0.5, -2.0
str text "Hello", "Ola"
bool yes or no true, false
char a single letter or sign 'A', 'z', '?'

Finch figures out the type by itself when you use :=:

name := "Ola"       // str
age := 10           // int
height := 1.42      // float
likes_cats := true  // bool

You can also write the type yourself, in front of the name:

int age = 10
str name = "Ola"

Both ways do the same thing. Use the one you like.

A box keeps its type. If age holds whole numbers, you cannot put text in it later. Finch will stop and tell you, which helps you catch mistakes early.


7. Doing math

Sign Meaning Example Result
+ add 7 + 2 9
- subtract 7 - 2 5
* multiply 7 * 2 14
/ divide 7 / 2 3
% what is left after dividing 7 % 2 1

Careful with dividing whole numbers: 7 / 2 gives 3, not 3.5, because both are whole numbers, so the answer is a whole number too (the part after the dot is dropped). If you want 3.5, use a decimal number: 7.0 / 2.

Multiplication and division happen before adding and subtracting, just like at school. Use brackets to change the order:

print(2 + 3 * 4)     // 14
print((2 + 3) * 4)   // 20

% is handy to check if a number is even: an even number divided by 2 leaves nothing, so n % 2 is 0.

You cannot divide by zero. If your program tries, it stops and tells you which line did it.


8. Making decisions: if

Programs can choose what to do. if means "only do this when something is true".

fn main() {
    temperature := 25

    if temperature > 20 {
        print("It's warm, wear a t-shirt")
    }
}

Add else for "otherwise, do this":

if temperature > 20 {
    print("It's warm")
} else {
    print("Take a jacket")
}

And else if for more choices. Finch checks them from top to bottom and does only the first one that is true:

if temperature > 25 {
    print("Hot!")
} else if temperature > 15 {
    print("Nice")
} else {
    print("Cold")
}

Comparing things

Sign Means
== is equal to
!= is not equal to
< is smaller than
> is bigger than
<= is smaller than or equal to
>= is bigger than or equal to

⚠️ One = puts a value in a box. Two == ask "are these equal?"

You can compare text too: if name == "Ola" { ... }.

Joining questions

  • && means and: both must be true. if age > 6 && age < 12 { ... }
  • || means or: at least one must be true. if day == "Sat" || day == "Sun" { ... }
  • ! means not: it flips true and false. if !raining { ... }

9. Repeating things: loops

Computers never get bored. A loop repeats the same steps many times.

Counting with for

fn main() {
    for i in 1..6 {
        print("Number", i)
    }
}
Number 1
Number 2
Number 3
Number 4
Number 5

for i in 1..6 means: "count with i from 1, up to but not including 6". So 0..10 gives you ten numbers: 0, 1, 2 … 9.

Repeating while something is true: while

fn main() {
    count := 3
    while count > 0 {
        print(count)
        count -= 1
    }
    print("Go!")
}
3
2
1
Go!

⚠️ Make sure the loop can end. If count never went down, the loop would run forever. If that happens, press Ctrl + C in the Terminal to stop it.

Stopping early and skipping

  • break leaves the loop right away.
  • continue skips the rest of this round and goes to the next one.
for i in 1..100 {
    if i == 5 {
        break         // stop when we reach 5
    }
    print(i)          // prints 1 2 3 4
}

10. Your own commands: functions

When you do the same thing in many places, give it a name. That named block is a function. You already know one: main.

fn say_hello(str name) {
    print("Hello,", name)
}

fn main() {
    say_hello("Ola")
    say_hello("Tom")
}
Hello, Ola
Hello, Tom

str name in brackets means: "this function needs one piece of text, and inside it we call that text name." The thing you put in brackets when using it is passed in.

Functions that give back an answer

Write -> and the type of the answer, and use return to give it back:

fn add(int a, int b) -> int {
    return a + b
}

fn main() {
    result := add(2, 3)
    print(result)          // 5
}

It does not matter where in the file you put your functions, above or below main.


11. Lists: many values in one box

Sometimes one box is not enough. A shopping list has many things on it. A list (programmers also say array) keeps many values in order:

fn main() {
    fruits := ["apple", "banana", "cherry"]
    print(fruits)
    print("I have", fruits.len, "fruits")
    print("The first one is", fruits[0])
}
["apple", "banana", "cherry"]
I have 3 fruits
The first one is apple
  • Square brackets [ ] make a list. Commas separate the values.
  • .len tells you how many things are in the list.
  • fruits[0] is the first thing. Counting starts at 0, so fruits[1] is the second, and fruits[2] the third.

If you ask for a place that doesn't exist, like fruits[10], the program stops and tells you: index 10 is out of range (the length is 3).

Adding and taking away

fn main() {
    []int scores          // an empty list of whole numbers
    scores.push(10)       // put 10 at the end
    scores.push(25)
    scores.push(7)
    print(scores)         // [10, 25, 7]

    last := scores.pop()  // take the last one out
    print(last, scores)   // 7 [10, 25]

    scores.sort()
    print(scores)
}

Going through a list

for ... in visits every value, one by one:

fn main() {
    total := 0
    for n in [3, 5, 2] {
        total += n
    }
    print("Total:", total)    // Total: 10
}

If you also want to know where you are in the list, give for two names: the first one counts 0, 1, 2…, the second one is the value:

fn main() {
    for i, fruit in ["apple", "banana", "cherry"] {
        print(i + 1, fruit)
    }
}
1 apple
2 banana
3 cherry

Lists can do more: contains, find, insert, remove, sort, reverse, join. The guide for technicians lists them all.

Looking things up by name: maps

A list finds things by their place: fruits[0]. Sometimes you want to find things by a name instead, like looking up a word in a dictionary, or a phone number in a phone book. That's a map: each key (the name) has a value:

fn main() {
    phone := ["Ola": "555-1234", "Jan": "555-9876"]
    print(phone["Ola"])          // 555-1234

    phone["Ewa"] = "555-0000"    // add a new name
    print(phone.len)             // 3

    if phone.has("Tom") {
        print("Tom is here")
    } else {
        print("no Tom")
    }

    for name, number in phone {  // every name with its number
        print(name, number)
    }
}
  • ["key": value, ...] makes a map. Keys are often text, but numbers work too.
  • phone["Ola"] gets the value for a key. Asking for a key that isn't there stops the program, so check first with phone.has("Tom"), or use phone.get("Tom", "unknown").
  • for name, number in phone visits every key with its value, in the order you added them.

Maps are great for counting. A new key starts at 0, so += 1 just works:

fn main() {
    map[str]int votes            // an empty map: text keys, whole-number values
    for v in ["cat", "dog", "cat", "cat", "fish"] {
        votes[v] += 1
    }
    print(votes)                 // {"cat": 3, "dog": 1, "fish": 1}
}

12. Playing with text

Text can be glued together with +:

fn main() {
    name := "Ola"
    greeting := "Hello, " + name + "!"
    print(greeting)
}

To glue a number to text, turn the number into text first with str(...):

age := 10
print("Age: " + str(age))

Text has handy tools too:

fn main() {
    word := "Finch"
    print(word.len)            // 5 letters
    print(word[0])             // F  (the first letter)
    print(word.upper())        // FINCH
    print(word.lower())        // finch
    print(word.contains("in")) // true
    for letter in word {
        print(letter)          // F, i, n, c, h, one per line
    }
}

13. Asking the user

input shows a question and waits until the person types an answer and presses Enter:

fn main() {
    name := input("What's your name? ")
    print("Nice to meet you,", name)
}

The answer is always text. If you need a number, turn it into one with int(...):

fn main() {
    answer := input("How old are you? ")
    age := int(answer)
    print("Next year you will be", age + 1)
}

If the person types something that isn't a number, the program stops with a message like can't turn "abc" into int.


14. Your own kinds of values: structs

Imagine a card in a library: each card has a title, an author and a year. A struct lets you make your own kind of value with named parts:

struct Book {
    str title
    str author
    int year
}

fn main() {
    b := Book(title: "The Hobbit", author: "Tolkien", year: 1937)
    print(b.title, "by", b.author)
    b.year = 1938
    print(b)
}
The Hobbit by Tolkien
Book(title: "The Hobbit", author: "Tolkien", year: 1938)
  • struct Book { ... } describes what every book has. Put it outside main.
  • Book(title: ..., author: ..., year: ...) makes one book.
  • b.title reads one part. b.year = 1938 changes it.

You can give parts a starting value. Then you don't have to fill them in:

struct Player {
    str name
    int lives = 3
}

fn main() {
    p := Player(name: "Ola")
    print(p.lives)     // 3
}

And you can keep many of them in a list: []Book shelf, then shelf.push(b).

Commands for your struct: methods

A struct can have its own commands. Write the struct's name, a dot, and the command's name. Inside, self is the struct the command was used on:

struct Player {
    str name
    int lives = 3
}

fn Player.hit() {
    self.lives -= 1
    print(self.name, "has", self.lives, "lives left")
}

fn Player.is_alive() -> bool {
    return self.lives > 0
}

fn main() {
    p := Player(name: "Ola")
    p.hit()                  // Ola has 2 lives left
    p.hit()                  // Ola has 1 lives left
    print(p.is_alive())      // true
}

p.hit() changes p itself, so after two hits p.lives really is 1.


15. Saving to a file

Programs forget everything when they end. To remember things, save them to a file:

fn main() {
    write_file("note.txt", "Buy milk")
    print(read_file("note.txt"))
}
  • write_file(name, text) saves the text in a file (and replaces what was there).
  • read_file(name) gives you the file's text back.
  • file_exists(name) tells you if the file is there (true or false).

The file appears in the folder where you ran the program.


16. When something goes wrong

Everyone makes mistakes when programming, even experts, every day. Finch tries hard to explain what went wrong in plain words. Example:

fn main() {
    age := 10
    print(agee)
}
hello.fch:3:11: error: there is no variable named 'agee'
    3 |     print(agee)
      |           ^

How to read it:

  • hello.fch is the file,
  • 3 is the line number,
  • 11 is how far into the line (the column),
  • then the explanation, and the line itself with a ^ pointing at the problem.

Here we simply misspelled age.

Common mistakes

Finch says What it means Fix
there is no variable named 'x' The name is misspelled, or you never made that box Check spelling. Create it with x := ...
a variable named 'x' already exists here You used := twice for the same name Use = the second time
'x' must be int, but this is str Wrong kind of value for that box Put the right kind of value in
put each statement on its own line Two commands on one line Press Enter between them
this '{' is never closed A } is missing Add the }
a condition must be bool if needs a yes/no question Compare something: if x > 0
runtime error: division by zero You divided by 0 while the program ran Check the number before dividing
index 5 is out of range (the length is 3) You asked a list for a place it doesn't have Remember counting starts at 0; check .len
can't turn "abc" into int int(...) got text that isn't a number Check what the person typed
can't use '+' on str and int Text plus a number Use str(...) on the number
'f' can fail, so say what happens then You used a command that can fail without saying what to do Add or ... (see below)

Things that can fail: or and try

Some things can go wrong while the program runs: the person types abc where you wanted a number, or a file isn't there. Instead of stopping the program, you can say what to do then, with or:

fn main() {
    age := int(input("Your age? ")) or 0        // not a number? use 0
    print("Next year you'll be", age + 1)

    note := read_file("note.txt") or "(no note yet)"
    print(note)
}

or can also be followed by a block. Inside it, err is the message that says what went wrong:

fn main() {
    while true {
        n := int(input("Pick a number: ")) or {
            print("That's not a number, try again")
            continue
        }
        print("You picked", n)
        break
    }
}

Your own functions can fail too. Put ! after the result type, and use return error("..."):

fn safe_divide(int a, int b) -> int! {
    if b == 0 {
        return error("can't divide by zero")
    }
    return a / b
}

fn main() {
    print(safe_divide(10, 2) or -1)     // 5
    print(safe_divide(10, 0) or -1)     // -1
}

Finch won't let you forget: calling safe_divide(10, 0) without or is an error that tells you to handle it. (You can also pass the problem on with try; the guide for technicians explains it.)


17. Small projects to try

Try writing each one yourself first. Look at the solution only if you get stuck.

Project 1: Times table

Show the 7 times table, from 7 × 1 to 7 × 10.

Solution
fn main() {
    for i in 1..11 {
        print("7 x", i, "=", 7 * i)
    }
}

Project 2: Even or odd

For every number from 1 to 10, show whether it is even or odd.

Solution
fn main() {
    for n in 1..11 {
        if n % 2 == 0 {
            print(n, "is even")
        } else {
            print(n, "is odd")
        }
    }
}

Project 3: Celsius to Fahrenheit

Show a table from 0 °C to 100 °C, every 10 degrees. Formula: F = C * 9 / 5 + 32.

Solution
fn to_fahrenheit(float c) -> float {
    return c * 9 / 5 + 32
}

fn main() {
    for c in 0..11 {
        celsius := float(c * 10)
        print(celsius, "C =", to_fahrenheit(celsius), "F")
    }
}

float(...) turns a whole number into a decimal number.

Project 4: Fizz Buzz

Count from 1 to 20. For numbers divisible by 3 say Fizz, by 5 say Buzz, by both say FizzBuzz, otherwise say the number.

Solution
fn main() {
    for n in 1..21 {
        if n % 15 == 0 {
            print("FizzBuzz")
        } else if n % 3 == 0 {
            print("Fizz")
        } else if n % 5 == 0 {
            print("Buzz")
        } else {
            print(n)
        }
    }
}

Project 5: Biggest of three

Write a function biggest(int a, int b, int c) -> int that gives back the largest number.

Solution
fn biggest(int a, int b, int c) -> int {
    if a >= b && a >= c {
        return a
    }
    if b >= c {
        return b
    }
    return c
}

fn main() {
    print(biggest(4, 9, 2))   // 9
}

Project 6: Guess the number

The computer picks a secret number. The player guesses until they find it, and the computer says "higher" or "lower" each time.

Solution
fn main() {
    secret := 42
    tries := 0
    while true {
        guess := int(input("Your guess: "))
        tries += 1
        if guess < secret {
            print("Higher!")
        } else if guess > secret {
            print("Lower!")
        } else {
            print("Yes! You needed", tries, "tries")
            return
        }
    }
}

return inside main ends the program. (A version with a random number is in examples/guess.fch.)

Project 7: Class average

Ask for 3 grades, keep them in a list, and show the list and the average.

Solution
fn main() {
    []int grades
    for i in 0..3 {
        grades.push(int(input("Grade: ")))
    }
    sum := 0
    for g in grades {
        sum += g
    }
    print("Grades:", grades)
    print("Average:", float(sum) / grades.len)
}

18. Cheat sheet

// a comment

fn main() {                       // the program starts here
    print("text", 42)             // show things

    x := 5                        // new box (type guessed)
    int y = 10                    // new box (type written)
    x = 7                         // change a box
    x += 1                        // add 1 to x

    if x > 5 {                    // decide
        print("big")
    } else if x > 0 {
        print("small")
    } else {
        print("zero or less")
    }

    for i in 0..10 {              // count 0 to 9
        print(i)
    }

    while x > 0 {                 // repeat while true
        x -= 1
    }
}

fn add(int a, int b) -> int {     // your own command
    return a + b
}

struct Pet {                      // your own kind of value
    str name
    int age = 1
}

// lists:  nums := [1, 2, 3]   nums.push(4)   nums[0]   nums.len   for n in nums { }   for i, n in nums { }
// maps:   ages := ["Ola": 9]   ages["Jan"] = 10   ages["Ola"]   ages.has("Tom")   for name, age in ages { }
// text:   "a" + "b"   str(42)   int("42")   s.len   s.upper()   s.contains("x")
// ask:    name := input("Name? ")
// files:  write_file("f.txt", text)   read_file("f.txt")
// methods: fn Pet.grow() { self.age += 1 }    p.grow()
// failing: n := int(text) or 0      x := read_file("f.txt") or { print(err) ... }

19. Words you will hear

Word Meaning
program A list of steps for the computer
code The text of a program
run Make the computer do the steps
compile Translate your code into something the computer can run. finch run does this for you
variable A named box holding a value
type The kind of value: number, text, yes/no…
function A named group of steps you can use again
loop Steps that repeat
condition A yes/no question, like x > 5
list / array Many values kept in order, like [1, 2, 3]
index The position of a value in a list, starting at 0
struct Your own kind of value made of named parts
method A command that belongs to a struct, like p.hit()
map Values you find by a key (a name), like a dictionary
input What the person types into the program
error A message saying something is wrong, and where
bug A mistake in a program

What next? When this all feels easy, move on to the guide for technicians. It covers every part of Finch.