# Gizlang language guide

This guide is for developers who know TypeScript, JavaScript or Go. You do not need to know Gizlang.

- Part 1 is a tutorial. In approximately 15 minutes, you write, run and test a small program.
- Part 2 is a tour of the main features. Each section is short and has one example.

Every example on this page was compiled and run with the Gizlang toolchain. The output under each example is the real output.

# Tutorial: your first Gizlang program

In this tutorial, you write a small shopping cart program. It calculates a total and refuses a payment that is over a budget. Then you add tests.

## Before you start

You need the `giz` command. To get it, refer to the [installation guide](../start.md). To try a program without an installation, use the [Playground](https://gizstack.com/play).

## Step 1: Hello, world

Make a file with the name `hello.giz`:

```gizlang
function main(): void {
    print("Hello, Gizlang.");
}
```

Run it:

```sh
giz hello.giz
```

```text
Hello, Gizlang.
```

- `main` is the entry point.
- `: void` tells that the function gives no result.
- `print` writes a line to the console.

## Step 2: A function and a type

Make a file with the name `cart.giz`:

```gizlang
type Item = { name: string; price: i64; qty: i64; };

function lineTotal(item: Item): i64 {
    return item.price * item.qty;
}

function main(): void {
    const pen: Item = { name: "pen", price: 3, qty: 2 };
    print(pen.name + ": " + string(lineTotal(pen)));
}
```

```sh
giz cart.giz
```

```text
pen: 6
```

- `type Item = { ... }` declares a record. A record is a value with fixed fields.
- `i64` is a 64-bit integer.
- Each parameter and each result has a type.
- `string(x)` converts a number to text. Gizlang does not convert types automatically. `"a" + 1` is a compile error.

## Step 3: A list of items

Replace `main` and add `cartTotal`:

```gizlang
type Item = { name: string; price: i64; qty: i64; };

function lineTotal(item: Item): i64 {
    return item.price * item.qty;
}

function cartTotal(items: Vec<Item>): i64 {
    let sum = 0;
    for (const item of items) {
        sum += lineTotal(item);
    }
    return sum;
}

function main(): void {
    let items: Vec<Item> = [{ name: "pen", price: 3, qty: 2 }];
    items.push({ name: "pad", price: 5, qty: 1 });
    print(string(items.length) + " items, total " + string(cartTotal(items)));
}
```

```text
2 items, total 11
```

- `Vec<Item>` is a list of `Item` values. You can also write `Item[]`.
- `let` declares a variable that you can change. `const` declares a value that you cannot change.
- `items.push(x)` adds `x` to a `let` list in place.

## Step 4: Errors

A function that can fail says so in its signature: `throws` and the type of the error. Add a `checkout` function and a `report` function:

```gizlang
import {Result} from "gizlang:core";

type Item = { name: string; price: i64; qty: i64; };

function lineTotal(item: Item): i64 {
    return item.price * item.qty;
}

function cartTotal(items: Vec<Item>): i64 {
    let sum = 0;
    for (const item of items) {
        sum += lineTotal(item);
    }
    return sum;
}

function checkout(items: Vec<Item>, budget: i64): i64 throws string {
    const total = cartTotal(items);
    if (total > budget) {
        throw "over budget by " + string(total - budget);
    }
    return budget - total;
}

function report(items: Vec<Item>, budget: i64): void {
    try {
        const left = checkout(items, budget);
        print("paid, " + string(left) + " left");
    } catch (message) {
        print("refused: " + message);
    }
}

function main(): void {
    const items: Vec<Item> = [
        { name: "pen", price: 3, qty: 2 },
        { name: "pad", price: 5, qty: 1 },
    ];
    report(items, 20);
    report(items, 10);
}
```

```text output
paid, 9 left
refused: over budget by 1
```

- `: i64 throws string` means the function gives an `i64` or fails with a `string`. `throw` fails it.
- A call gives the `i64`. When the call fails, the `try` block goes to its `catch`. A function that does not catch the failure must be a `throws` function itself, so the failure goes on to its caller.
- Under the signature, the function returns a `Result<i64, string>`. A place that expects a `Result` takes it as it is, and `match` reads its cases. A `match` must write one arm for each case.

## Step 5: Tests

A test is a function with a name that starts with `test`. It has no parameters and the result `void`.

1. Add the import of `gizlang:test` as line 2 of `cart.giz`, and two test functions at the end. The file is now:

   ```gizlang
   import {Result} from "gizlang:core";
   import * as Test from "gizlang:test";

   type Item = { name: string; price: i64; qty: i64; };

   function lineTotal(item: Item): i64 {
       return item.price * item.qty;
   }

   function cartTotal(items: Vec<Item>): i64 {
       let sum = 0;
       for (const item of items) {
           sum += lineTotal(item);
       }
       return sum;
   }

   function checkout(items: Vec<Item>, budget: i64): i64 throws string {
       const total = cartTotal(items);
       if (total > budget) {
           throw "over budget by " + string(total - budget);
       }
       return budget - total;
   }

   function report(items: Vec<Item>, budget: i64): void {
       try {
           const left = checkout(items, budget);
           print("paid, " + string(left) + " left");
       } catch (message) {
           print("refused: " + message);
       }
   }

   function main(): void {
       const items: Vec<Item> = [
           { name: "pen", price: 3, qty: 2 },
           { name: "pad", price: 5, qty: 1 },
       ];
       report(items, 20);
       report(items, 10);
   }

   function testCartTotal(): void {
       const items: Vec<Item> = [{ name: "pen", price: 3, qty: 4 }, { name: "pad", price: 5, qty: 1 }];
       Test.equal(cartTotal(items), 17);
   }

   function testCheckoutRefusesOverBudget(): void {
       const items: Vec<Item> = [{ name: "pen", price: 3, qty: 2 }];
       const refused: Result<i64, string> = Result.err("over budget by 2");
       const answer: Result<i64, string> = checkout(items, 4);
       Test.equal(answer, refused);
   }
   ```

2. Run the tests:

   ```sh
   giz test cart.giz
   ```

   ```text
   0 cached, 2 run, 0 failed
   ```

3. Run the tests again:

   ```text
   2 cached, 0 run, 0 failed
   ```

   The runner records each pass. It runs a test again only when the code that the test uses changes.

To see a failure, change `17` to `18` and run the tests:

```text
fail testCartTotal R004
    gizlang: R004 in equal line 2 (actual = 17, expected = 18) <- testCartTotal (cart.giz:44)
    gizlang: replay: function testReplayEqual(): void { equal(17, 18); }
test runner failed or returned an incomplete verdict (1/1)
1 cached, 1 run, 2 failed
```

- The failure shows the actual and expected values, and the line of the test.
- The `replay` line is a test that repeats the failed call.
- The command exits with status 1.

Change `18` back to `17`.

## Step 6: Run and check

- `giz cart.giz` builds and runs the program.
- `giz test cart.giz` runs the tests of the file.
- `giz check cart.giz` checks the file for errors without a run of `main`. When the check passes, it also runs the tests that changed.

You wrote a program with a record type, a list, a loop, error handling and tests. The tour that follows shows more of the language.

---

# Language tour

## Values and types

```gizlang
function main(): void {
    const count: i64 = 7;
    const ratio: f64 = 0.5;
    const big: u64 = 18446744073709551615u;
    const ready: bool = true;
    const name: string = "Gizlang";
    const raw: bytes = b"GIZ";
    print(string(count / 2));
    print(string(f64(count) * ratio));
    print(string(big));
    print(string(ready));
    print(name + " has " + string(name.length) + " bytes");
    print(string(raw.length));
    print(string("한글".length));
}
```

```text
3
3.5
18446744073709551615
true
Gizlang has 7 bytes
3
6
```

| Type | Meaning |
| --- | --- |
| `i64`, `u64` | Checked 64-bit integers. Write a `u64` literal with the suffix `u`. |
| `f64` | A 64-bit IEEE float. `number` is the same type. |
| `bool` | `true` or `false`. `boolean` is the same type. |
| `string` | UTF-8 text. `.length` and indexes count bytes, not characters. |
| `bytes` | Immutable bytes. Write a literal as `b"..."`. |

- Integer `/` truncates.
- There are no implicit conversions. Use `i64(x)`, `u64(x)`, `f64(x)` and `string(x)`.
- Integer overflow, division by zero and a bad index are faults. A fault stops the program. It is not a value.

```gizlang
function double(x: i64): i64 {
    return x * 2;
}
function main(): void {
    print(string(double(9223372036854775807)));
}
```

```text
gizlang: R003 in double line 2 (x = 9223372036854775807) <- main line 2
gizlang: replay: function testReplayDouble(): void { double(9223372036854775807); }
runtime R003
```

The fault report names the function, its arguments and the call path. The program exits with status 1.

## Variables

```gizlang
const LIMIT = 3;

function main(): void {
    const greeting = "hi";
    let total = 0;
    total += LIMIT;
    total = total * 10;
    let words: Vec<string> = [];
    words.push(greeting);
    words.push("there");
    print(string(total) + " " + string(words.length));
}
```

```text
30 2
```

- `const` declares an immutable binding. `let` declares a mutable binding.
- A module-level `const` must be a literal. Module-level `let` is not available.
- You cannot assign to a `const`:

```gizlang error=E_IMMUTABLE
function main(): void {
    const total = 1;
    total = 2;
    print(string(total));
}
```

```sh
giz check const_error.giz
```

```text
const_error.giz:3:5: error E_IMMUTABLE in main: cannot assign to, or pass as mut, a const binding or an immutable value: declare it with let
        total = 2;
        ^^^^^
```

- An inner scope cannot declare a name again:

```gizlang error=E_SHADOW
function main(): void {
    const total = 1;
    if (total > 0) {
        const total = 5;
        print(string(total));
    }
}
```

```text
shadow.giz:4:15: error E_SHADOW in main: a local cannot reuse a name already in scope; rename it
            const total = 5;
                  ^^^^^
```

## Functions and closures

```gizlang
function add(a: i64, b: i64): i64 {
    return a + b;
}

function apply(f: (x: i64) => i64, value: i64): i64 {
    return f(value);
}

function makeAdder(n: i64): (x: i64) => i64 {
    return (x: i64): i64 => { return x + n; };
}

function main(): void {
    const addTen = makeAdder(10);
    const double = (x: i64): i64 => { return x * 2; };
    print(string(add(2, 3)));
    print(string(apply(addTen, 5)));
    print(string(apply(double, 5)));
    const steps: Vec<(x: i64) => i64> = [addTen, double];
    let value = 1;
    for (const step of steps) {
        value = step(value);
    }
    print(string(value));
    print(string([1, 2, 3].map(double).length));
}
```

```text
5
15
10
22
3
```

- Declare a function with `function name(param: Type): Result`.
- An arrow function `(x: T): R => { ... }` is a closure.
- A function type `(x: T) => R` is a value type. A parameter, a result, a field or a `Vec` element can have it.
- A closure copies the values that it captures. It cannot capture a `let` local:

```gizlang
function main(): void {
    let count = 0;
    const next = (): i64 => { return count + 1; };
    print(string(next()));
}
```

```text
capture.giz:3:38: error E_ESCAPE in main: a closure cannot capture a mutable local or a view `count`: bind a const copy before the closure (const copy = count;) and use that
        const next = (): i64 => { return count + 1; };
                                         ^^^^^
```

## Records and unions

```gizlang
type Point = { x: i64; y: i64; };

type Shape =
    | { kind: "circle"; radius: f64; }
    | { kind: "rect"; width: f64; height: f64; };

function Point.add(self: Point, other: Point): Point {
    return { x: self.x + other.x, y: self.y + other.y };
}

function moveRight(p: mut Point, dx: i64): void {
    p.x += dx;
}

function area(shape: Shape): f64 {
    match (shape) {
        Shape.circle(radius) => { return 3.14159 * radius * radius; }
        Shape.rect(width, height) => { return width * height; }
    }
}

function main(): void {
    const a: Point = { x: 1, y: 2 };
    let b = a;
    b.x = 10;
    moveRight(b, 5);
    const c = a + b;
    const d = { ...c, y: 0 };
    print(string(a.x) + " " + string(b.x) + " " + string(c.x) + "," + string(c.y) + " " + string(d.y));
    const same: Point = { x: 1, y: 2 };
    print(string(a === same));
    const shapes: Vec<Shape> = [Shape.circle(1.0), { kind: "rect", width: 2.0, height: 3.0 }];
    for (const s of shapes) {
        print(string(area(s)));
    }
    print(string(shapes[1].kind === "rect"));
}
```

```text
1 15 16,4 0
true
3.14159
6
true
```

- Records are values. `let b = a` copies `a`. A change to `b` does not change `a`.
- A `let` local changes in place at any field path, for example `b.x = 10`.
- A function changes a value of its caller only through a `mut` parameter.
- `function Point.add(self: Point, ...)` declares a method. Call it as `a.add(b)`.
- An operator on a record is its method of that name: `a + b` is `a.add(b)`. The others are `- * /` (sub, mul,
  div), `< <= > >=` (lt, le, gt, ge) and unary `-` (neg).
- `{ ...c, y: 0 }` copies `c` and replaces one field. The spread source must be a name, a field or an element, not a call.
- A union is a set of cases. The string field `kind` names the case. Make a case with `Shape.circle(1.0)` or with an object literal that has an expected type.
- `===` compares records by their fields.
- There are no classes and no inheritance.

## Interfaces and generics

```gizlang
import * as Text from "gizlang:text";

interface Named {
    label(self: Self): string;
}

type User = { name: string; };
type Team = { name: string; size: i64; };

function User.label(self: User): string {
    return "user " + self.name;
}

function Team.label(self: Team): string {
    return "team " + self.name + " (" + string(self.size) + ")";
}

function shout<T extends Named>(item: T): string {
    return item.label().toUpperCase();
}

type Box<T> = { value: T; };

function main(): void {
    const u: User = { name: "ana" };
    const t: Team = { name: "core", size: 4 };
    const all: Vec<Named> = [u, t];
    for (const n of all) {
        print(n.label());
    }
    print(shout(t));
    const b: Box<string> = { value: "boxed" };
    print(b.value);
}
```

```text
user ana
team core (4)
TEAM CORE (4)
boxed
```

- An interface is a set of methods. Each method has `self: Self` as its first parameter.
- A type implements an interface when it has those methods. You do not declare `implements`.
- Use an interface as a bound (`<T extends Named>`) or as a type (`Vec<Named>`).
- The compiler specializes generics for each type argument.
- String methods such as `toUpperCase` come from `gizlang:text`, which every program has.

## Collections

```gizlang
function main(): void {
    const nums: Vec<i64> = [5, 3, 8, 1];
    const big = nums.filter(n => n > 2);
    const total = nums.reduce((acc, n) => acc + n, 0);
    const order = nums.toSorted((a, b) => a - b);
    print(`${big.length} ${total} ${order[0]}`);
    print(`${nums.findIndex(n => n === 8)} ${nums.includes(3)} ${nums.at(-1) ?? 0}`);
    print(string(nums.slice(1, 3).length));

    const words = "the cat saw the dog".split(" ");
    const counts = new Map<string, i64>();
    for (const w of words) {
        counts.merge(w, 1, (a, b) => a + b);
    }
    print(`${counts.get("the") ?? 0} ${counts.get("cow") ?? 0}`);
    print(counts.keys().join(","));

    const seen = new Set<string>();
    seen.add("a");
    seen.add("a");
    print(`${seen.size} ${seen.has("a")}`);
}
```

```text
3 17 1
2 true 1
2
2 0
the,cat,saw,dog
1 true
```

- `Vec<T>` is a value. `v.map(f)`, `v.slice(start, end)`, `v.length` and `v.push(x)` are built in.
- `filter`, `find`, `findIndex`, `some`, `every`, `reduce`, `includes`, `indexOf`, `at`, `concat`, `sorted` and `count`
  come from `gizlang:collections`, which every program has: call them as methods with no import.
- `findIndex` and `indexOf` give -1 when no element matches. `find` and `at` give an optional value.
- `new Map<K, V>()` and `new Set<K>()` make a map and a set. They keep insertion order.
- `m.get(k)` gives an optional value. `m.get(k) ?? fallback` reads it with a default.

## Control flow

```gizlang
function label(n: i64): string {
    if (n % 15 === 0) {
        return "FizzBuzz";
    } else if (n % 3 === 0) {
        return "Fizz";
    } else if (n % 5 === 0) {
        return "Buzz";
    }
    return string(n);
}

function main(): void {
    let line = "";
    for (const i of range(1, 16)) {
        if (i === 8) {
            continue;
        }
        line = line + label(i) + " ";
    }
    print(line);
    let n = 27;
    let steps = 0;
    while (n !== 1) {
        let next = n / 2;
        if (n % 2 === 1) {
            next = 3 * n + 1;
        }
        n = next;
        steps += 1;
        if (steps > 1000) {
            break;
        }
    }
    print("steps: " + string(steps));
}
```

```text
1 2 Fizz 4 Buzz Fizz 7 Fizz Buzz 11 Fizz 13 14 FizzBuzz 
steps: 111
```

- `if`, `else if`, `while`, `break` and `continue` work as in TypeScript.
- `for (const i of range(lo, hi))` counts from `lo` to `hi - 1`.
- `for (const x of v)` reads each element of a `Vec`.
- `match` reads a union. Refer to [Records and unions](#records-and-unions).
- The `? :` operator and `switch` are not available. Use `if` and `match`.

## Errors: Result, Option and try

```gizlang
import {Result, Option} from "gizlang:core";
import {parseI64, IntegerParse} from "gizlang:text";

function parseAge(text: string): Result<i64, string> {
    match (parseI64(text)) {
        IntegerParse.ok(n) => {
            if (n < 0) {
                return Result.err("negative: " + text);
            }
            return Result.ok(n);
        }
        IntegerParse.invalid(at) => { return Result.err("not a number: " + text); }
        IntegerParse.overflow(at) => { return Result.err("too large: " + text); }
    }
}

function ageNextYear(text: string): Result<i64, string> {
    const age = try parseAge(text);
    return Result.ok(age + 1);
}

function firstLong(words: Vec<string>): Option<string> {
    for (const w of words) {
        if (w.length > 3) {
            return Option.some(w);
        }
    }
    return Option.none();
}

function main(): void {
    for (const input of ["41", "-2", "abc"]) {
        match (ageNextYear(input)) {
            Result.ok(n) => { print("next year: " + string(n)); }
            Result.err(e) => { print("error: " + e); }
        }
    }
    print(string(ageNextYear("x").valueOr(0)));
    print(firstLong(["a", "tree"]) ?? "none");
    print(firstLong(["a"]) ?? "none");
    print(firstLong(["house"])!);
}
```

```text
next year: 42
error: negative: -2
error: not a number: abc
0
tree
none
house
```

- `Result<T, E>` has the cases `ok(value)` and `err(error)`. `Option<T>` has the cases `some(value)` and `none()`. Import them from `gizlang:core`.
- `try e` gives the value of `ok` or `some`. For any other case, `try` returns that case from the function. The result type of the function must have the same case.
- `result.valueOr(fallback)` and `option ?? fallback` give a default.
- `x!` gives the value. When there is no value, it faults.
- A match arm binds the whole payload of its case. `IntegerParse.invalid` has a payload (the byte position), so the arm is `IntegerParse.invalid(at)`.
- `throw` and `try`-`catch` work with `throws`: `function f(): T throws E` returns a `Result` that its callers take as `T` (see [Step 4: Errors](#step-4-errors)). `finally` runs when the statement ends.

## Modules and imports

A file is a module. Use `export` to make a declaration visible to other modules. Make a folder with these three files.

`geometry.giz`:

```gizlang title="geometry.giz"
/** A point on a grid. */
export type Point = { x: i64; y: i64; };

/** The Manhattan distance between two points. */
export function distance(a: Point, b: Point): i64 {
    return abs(a.x - b.x) + abs(a.y - b.y);
}

function abs(n: i64): i64 {
    if (n < 0) {
        return -n;
    }
    return n;
}
```

`main.giz`:

```gizlang title="main.giz"
import {Point, distance} from "./geometry.giz";
import * as Text from "gizlang:text";

function main(): void {
    const a: Point = { x: 0, y: 0 };
    const b: Point = { x: 3, y: -4 };
    const parts = [string(distance(a, b)).padStart(4, "."), "steps"];
    print(Text.join(parts, " "));
}
```

```sh
giz main.giz
```

```text output
...7 steps
```

`geometry.test.giz`:

```gizlang title="geometry.test.giz"
import {Point, distance} from "./geometry.giz";
import * as Test from "gizlang:test";

function testDistance(): void {
    const a: Point = { x: 1, y: 1 };
    const b: Point = { x: 4, y: 5 };
    Test.equal(distance(a, b), 7);
    Test.equal(distance(b, a), 7);
}
```

```sh
giz test geometry.test.giz
```

```text
0 cached, 1 run, 0 failed
```

- Import a file of your project by a relative path: `"./geometry.giz"`.
- Import an SDK module by name: `"gizlang:text"`, `"gizlang:collections"`, `"gizlang:core"`.
- Named imports (`{a, b}`), namespace imports (`* as Text`) and aliases work as in TypeScript.
- `abs` has no `export`. Other modules cannot import it.
- Put the tests of `X.giz` in `X.test.giz` beside it. `giz check X.giz` also runs them.
- `/** ... */` before a declaration is a doc comment. Tools read it.

## Packages

A package is a folder with a `package.json`. The manifest names the entry and the modules, and pins the sha256 of each source. Use the same `geometry.giz` and this `main.giz`:

```gizlang error=E_MODULE
import {Point, distance} from "geometry";

function main(): void {
    const a: Point = { x: 0, y: 0 };
    const b: Point = { x: 2, y: 5 };
    print("distance: " + string(distance(a, b)));
}
```

`package.json`:

```json
{
  "name": "shapes",
  "private": true,
  "gizlang": {
    "entry": { "path": "main.giz", "sha256": "" },
    "modules": {
      "geometry": { "path": "geometry.giz", "sha256": "" }
    }
  }
}
```

Write the pins, then run the package:

```sh
giz pin .
giz package.json
```

```text
distance: 7
```

- `giz pin DIR` writes the sha256 of each source into `package.json`.
- Inside a package, a module imports another module by its manifest name: `"geometry"`.
- When a source changes after the pin, the package does not build. `giz get --frozen DIR` names the file:

```sh
giz get --frozen .
```

```text
gizlang: geometry.giz is missing or does not match its sha256
```

Run `giz pin .` again after each intended change. For dependencies and registries, refer to the [package guide](packages.md).

## Tasks and channels

Gizlang has no `async` or `await`. A task is a closure that runs concurrently. `sleep`, `join` and I/O suspend only the calling task.

```gizlang
import * as Task from "gizlang:task";
import {Result, Option} from "gizlang:core";

function fib(n: i64): i64 {
    if (n < 2) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

function main(): void {
    const slow = Task.spawn(() => { Task.sleep(20); return fib(20); });
    const fast = Task.spawn(() => { return fib(10); });
    match (Task.join(fast)) {
        Result.ok(n) => { print("fast: " + string(n)); }
        Result.err(f) => { print("fast failed: " + string(f.code)); }
    }
    match (Task.join(slow)) {
        Result.ok(n) => { print("slow: " + string(n)); }
        Result.err(f) => { print("slow failed: " + string(f.code)); }
    }

    const lines = Task.channel<string>(4);
    Task.spawn(() => {
        for (const word of ["one", "two", "three"]) {
            Task.send(lines, word);
        }
        Task.close(lines);
        return 0;
    });
    let more = true;
    while (more) {
        match (Task.receive(lines)) {
            Option.some(word) => { print("got " + word); }
            Option.none() => { more = false; }
        }
    }

    const squares = Task.parallel(4, (i: i64): i64 => { return i * i; });
    print("squares: " + string(squares[3]));
}
```

```text
fast: 55
slow: 6765
got one
got two
got three
squares: 9
```

- `Task.spawn(f)` starts a task. `Task.join(t)` waits for it and gives a `Result`.
- `Task.channel<T>(capacity)` makes a bounded queue. `send` waits while the queue is full. `receive` gives `Option.none()` when the queue is closed and empty.
- `Task.parallel(n, body)` runs `body(i)` on the CPU threads and gives the results in index order. The body can only compute. It cannot do I/O or print.
- A task cannot live longer than the task that started it.

A fault in a task stops only that task. The parent gets it as `Result.err`:

```gizlang
import * as Task from "gizlang:task";
import {Result} from "gizlang:core";

function divide(a: i64, b: i64): i64 {
    return a / b;
}

function main(): void {
    const bad = Task.spawn(() => { return divide(1, 0); });
    match (Task.join(bad)) {
        Result.ok(n) => { print("value: " + string(n)); }
        Result.err(f) => { print("task fault: " + string(f.code)); }
    }
    print("main continues");
}
```

```text
task fault: 2
main continues
```

Fault 2 is division by zero.

## Files and the network

A program gets access to the outside world through one value: `main(sys: System)`. Library code cannot reach files, the network, the clock or the environment unless the program gives it the `System` value or a value made from it.

```gizlang
import {System, IoError} from "gizlang:system";
import {Result} from "gizlang:core";

function saveAndCount(sys: System, name: string): Result<i64, IoError> {
    const dir = try sys.files();
    try dir.writeFile(name, utf8("line one\nline two\n"));
    const data = try dir.readFile(name);
    const text = string(data);
    try dir.remove(name);
    return Result.ok(text.split("\n").length - 1);
}

function main(sys: System): void {
    const args = sys.arguments();
    print("arguments: " + string(args.length));
    match (saveAndCount(sys, "notes.txt")) {
        Result.ok(lines) => { print("lines: " + string(lines)); }
        Result.err(e) => { print("io error: " + e.message()); }
    }
    match (sys.files()) {
        Result.ok(dir) => {
            match (dir.readFile("missing.txt")) {
                Result.ok(data) => { print("read " + string(data.length)); }
                Result.err(e) => { print("io error: " + e.message()); }
            }
        }
        Result.err(e) => { print("io error: " + e.message()); }
    }
}
```

```sh
giz files.giz alpha beta
```

```text
arguments: 2
lines: 2
io error: no such file or directory
```

- `sys.files()` gives the working directory as a `Dir`. A `Dir` opens only paths below it: no `..`, no absolute paths.
- `readFile` gives `bytes`. `string(data)` converts valid UTF-8 to a `string`. `utf8(text)` converts a `string` to `bytes`.
- Each operation gives a `Result<T, IoError>`. `e.message()` gives the text of the error.
- `sys.arguments()` gives the command-line arguments after the file name.

A TCP server and a client in one program:

```gizlang
import {System, IoError} from "gizlang:system";
import {Result} from "gizlang:core";
import * as Task from "gizlang:task";

function serveOne(sys: System): Result<string, IoError> {
    const listener = try sys.listen("127.0.0.1", 0);
    const port = listener.port();
    const client = Task.spawn(() => {
        match (sys.connect("127.0.0.1", port)) {
            Result.ok(stream) => {
                stream.write(utf8("ping"));
                stream.close();
            }
            Result.err(e) => { print("connect failed: " + e.message()); }
        }
        return 0;
    });
    const conn = try listener.accept();
    const data = try conn.readAll();
    conn.close();
    listener.close();
    Task.join(client);
    return Result.ok(string(data));
}

function main(sys: System): void {
    match (serveOne(sys)) {
        Result.ok(text) => { print("server got: " + text); }
        Result.err(e) => { print("io error: " + e.message()); }
    }
}
```

```text
server got: ping
```

- `sys.listen(host, 0)` selects a free port. `listener.port()` gives it.
- `sys.connect(host, port)` opens a TCP `Stream`.
- `accept` and `readAll` suspend only the calling task.
- For HTTP, use `gizlang:http`. For TLS, use `gizlang:tls`.

## Markup

Markup is TSX in ordinary `.giz` files. It compiles to an `Html` value from `gizlang:html`. Text in holes is escaped.

```gizlang
import {Html, each, when} from "gizlang:html";

type Todo = { id: i64; title: string; done: bool; };

type ListProps = { title: string; };

function Row(t: Todo): Html {
    return <li data-id={t.id}>{t.title}{when(t.done, () => <b> (done)</b>)}</li>;
}

function TodoList(p: ListProps, children: Html): Html {
    return <section><h2>{p.title}</h2>{children}</section>;
}

function main(): void {
    const todos: Vec<Todo> = [
        { id: 1, title: "Write <docs>", done: true },
        { id: 2, title: "Ship", done: false },
    ];
    const page = <TodoList title="Today"><ul>{each(todos, (t: Todo) => Row(t))}</ul></TodoList>;
    print(page.text());
}
```

```text
<section><h2>Today</h2><ul><li data-id="1">Write &lt;docs&gt;<b> (done)</b></li><li data-id="2">Ship</li></ul></section>
```

- A lowercase tag writes an HTML element.
- A tag that starts with an uppercase letter calls a component function. The attributes are one record argument. The children are one `Html` argument.
- Markup has no spread, `&&`, `? :` or `.map`. Use `when(cond, f)`, `each(items, f)` and `raw(trustedText)`.
- `page.text()` gives the HTML text.

To serve pages, routes and static files, use Gizlang Web (`gizlang:http`). Refer to the [Gizlang Web guide](web.md).

## What differs from TypeScript

Gizlang uses the spelling of TypeScript. A TypeScript feature that Gizlang does not have is a compile error. It never gets a different meaning.

```gizlang
type Order = { id: string; qty: i64; };

function bump(o: mut Order): void {
    o.qty += 1;
}

function main(): void {
    const n = 3;
    let size = "small";
    if (n > 2) {
        size = "big";
    }
    print("n=" + string(n) + " is " + size);

    const first: Order = { id: "a1", qty: 1 };
    let second = first;
    bump(second);
    print(string(first.qty) + " " + string(second.qty));
    print(string(7 / 2) + " " + string(7.0 / 2.0));
}
```

```text
n=3 is big
1 2
3 3.5
```

| TypeScript | Gizlang |
| --- | --- |
| `number` | `i64` or `f64`. `number` is `f64`. Integer `/` truncates. |
| Implicit conversion, `"n=" + n` | `"n=" + string(n)`, or a template string `` `n=${n}` ``. |
| `null`, `undefined`, `?.`, `??`, optional fields | The same, on `Option<T>`: `x?: T` and `T \| undefined` are `Option<T>`. |
| `throw`, `try`-`catch` | The same, with `throws E` on the function: `function f(): T throws E`. A call to it gives the `T` and a failure returns from the caller. `finally` runs when the statement ends. |
| `class`, inheritance | Not available. Use records, methods (`function T.m(self: T)`) and interfaces. |
| `+`, `<` and the other operators on objects | An operator on a record calls its method: `a + b` is `a.add(b)`. |
| Objects shared by reference | Records, `Vec`, strings and bytes are values. Use a `mut` parameter to change a value of the caller. |
| `async`, `await` | Not available. Use `gizlang:task`. |
| `enum` | Not available. Use a union. |
| Array, `Map`, `Set` and string methods | The same names, with no import (`v.filter(f)`, `new Map<K, V>()`). `v.sort` takes a `before` function: `(a, b) => a < b`. |
| `any`, untyped values, `eval` | Compile errors. |
| Module-level `let` | Not available. |
| A name declared again in an inner scope | Compile error `E_SHADOW`. |
| A closure that changes an outer `let` | Compile error `E_ESCAPE`. |

## Next steps

- Use the [Playground](https://gizstack.com/play) to run programs without an installation.
- Read the [package guide](packages.md) for dependencies and pins.
- Read the [Gizlang Web guide](web.md) to serve a site.
