Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 115 additions & 32 deletions docs/language-overview/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,67 +242,150 @@ Pointers mirror C: `Foo*` is a pointer to a `Foo`, while `Foo**` is a pointer to

In addition to the standard properties, pointers also have the `::inner` property. It returns the type of the object pointed to as a type `typeid`.

### The `fault` type

`fault` type is like error code names in C, all in upper case name. `fault`s are defined by `faultdef`. Like other type related definitions,`faultdef` must come at global scope.

```c3
faultdef MY_ERR1, EOF, NOT_FOUND, PARSE_ERROR, IO_ERROR;

fault x = MY_ERR1;
fault y = x;
fault z = ERROR2; // Compile error: undefined fault

fn fault some_function()
{
return EOF;
}

fn void other function()
{
faultdef NEW_ERROR; // Compile error, `faultdef` must be at global scope
}
```

Like the [typeid type](#the-typeid-type), a `fault` is pointer sized
and each value defined by `faultdef` is globally unique. This is true even when faults are separately compiled.

!!! note
The underlying unique value assigned to a fault may vary each time a program is run.

#### Fault description

The fault type only has one field: `<fault>.description`, which returns the name of the fault in string literal, namespaced with the last module path, e.g. `"io::EOF"`.

```c3
module aa;
import std::io;

faultdef AA, BB;

fn void print_fault()
{
fault x = BB;
io::printfn("Error: %s", x); // prints "aa::BB"
}
```

### Optional

An [Optional type](../language-common/optionals-essential.md#what-is-an-optional) is created by taking a type and appending `~`.
An Optional type behaves like a tagged union, containing either the
Result or an Empty, which also carries a [fault](#the-fault-type) type.
An [Optional type](../language-common/optionals-essential.md#what-is-an-optional) behaves like a tagged union, containing either a value or a fault.

Once extracted, a `fault` can be converted to another `fault`.
Optional type is marked as `?` at the end of any type, like `int?` or `char[]*?`. Optional value must be assigned by either a value of the type or a `fault` with `~`.

```c3
faultdef MISSING; // define a fault
faultdef AA, BB, CC;

int? i;
i = 5; // Assigning a real value to i.
i = io::EOF~; // Assigning an optional result to i.
fault b = MISSING; // Assign a fault to b
b = @catch(i); // Assign the Excuse in i to b (EOF)
int? x = 3;
int? x = AA; // Compile error, ? must be attached to be an optional
int? x = AA~; // a `fault` optional
```

Only variables, expressions and function returns may be Optionals.
Function and macro parameters in their definitions may not be optionals.
Variables, expressions and return types may be Optionals, but parameters may not be optionals.

```c3
fn Foo*? getFoo() { /* ... */ } // ✅ Ok!
int? x = 0; // ✅ Ok!
fn void processFoo(Foo*? f) { /* ... */ } // ❌ fn parameter
```

An Optional value can use the special `if-try` and `if-catch` to unwrap its result or its Empty,
it is also possible to implicitly return if it is Empty using `!` and panic with `!!`.
An optional is in a superimposed (uncertain) state and it must be `unwrapped` to see whether it's a normal value or a fault.

To learn more about the Optional type and error handling in C3, read the page on [Optionals and error handling](../language-common/optionals-essential.md).
There are several ways to unwrap an optional variable.

!!! note
If you want a more regular "optional" value, to store in structs, then you can use the generic `Maybe` type in std::collections.
When you expect a normal value, use `if (try)`, and when you expect a fault, use `if (catch)`.

### The `fault` type
```c3
fn int? fallable_function() { ... }

fn void test1()
{
int? x = fallable_function(); // x is in uncertain state
if (try x) // unwrap x to a normal value
{
io::printfn("%d", x); // OK
}
// here x is still optional
int y = x; // Compile error, optional `int?` cannot be assigned to normal `int`
}

When an [Optional](../language-common/optionals-essential.md#what-is-an-optional) does not contain a result, it is Empty, but contains a `fault` which explains why there was no
normal value. A fault has the special property that together with the `~` suffix it creates an Empty value:
fn void test2()
{
int? x = fallable_function(); // x is in uncertain state
if (catch err = x) // unwrap x to a fault
{
// err is unwrapped fault
io::printfn("%s", err); // OK
fault y = err; // OK.
// x is optional here
}
else
{
// x is unwrapped value here
int z = x; // OK.
}
// here x is still optional
int y = x; // Compile error, optional `int?` cannot be assigned to normal `int`
}

```c3
int? x = IO_ERROR~; // 'IO_ERROR~' is an Optional Empty.
fault y = IO_ERROR; // Here IO_ERROR is just a regular
// value, since it isn't followed by '~'
fn void? test3() // returns optional
{
int? x = fallable_function();
if (catch err = x) // unwrap x to a fault
{
// err is unwrapped fault
io::printfn("%s", err); // OK
fault y = err; // OK.
// x is optional here
return err~; // rethrow as an optional
}
// x is unwrapped value here, when `if (catch)` block ends with `return`, `continue` or `break`
int z = x; // OK.
}
```

A new `fault` value can only be defined using the `faultdef` statement:
There are three operators to unwrap an optional.
* `!` rethrow operator, meaning 'or return optional'
* `!!` panic operator, meaning 'or panic'
* `??` or_else operator, meaning 'or else'

```c3
faultdef IO_ERROR, PARSE_ERROR, NOT_FOUND;
fn int? fallable() { ... }

fn void? test()
{
int? x = fallable();
int y = fallable()!; // assign int value or rethrow optional
int z = fallable()!!; // assign int value or panic print stacktrace
int w = fallable() ?? -1; // asign int value or else -1 as a default value
}
```

Like the [typeid type](#the-typeid-type), a `fault` is pointer sized
and each value defined by `faultdef` is globally unique. This is true even when faults are separately compiled.
To learn more about the Optional type and error handling in C3, read the page on [Optionals and error handling](../language-common/optionals-essential.md).

!!! note
The underlying unique value assigned to a fault may vary each time a program is run.

#### Fault description
If you want a more regular "optional" value, to store in structs, then you can use the generic `Maybe` type in std::collections.

The fault type only has one field: `<fault>.description`, which returns the name of the fault in string literal, namespaced with the last module path, e.g. `"io::EOF"`.

### The `typeid` type

Expand Down