Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Flint's error system is quite unique. Every function can fail. Absolutely every single function a user defines in his code can fail. Because every function can fail, Flint can deeply integrate error handling into the language. A function returning a str value, for example, actually returns a (err, str) value. The first implicit return type of any Flint function is the error value of said function. This error value, however, is completely hidden from the user outside of Flint's error handling syntax.

Flint has two keywords for error handling: throw and catch. But, unlike Java or C++, where the error handling happens outside the normal execution path ("happy path" / "unhappy path") which completely breaks execution consistency and results in lots of context switching for the CPU, Flint has it's error handling system built directly into the calling / returning code of every function, which makes it much faster than traditional exception-based error handling.

Flint's error handling system, however, is based around the idea of Error Sets. An error set is basically just a type, a set type, consisting of every possible value the error could have. Here is a small example of that:

use Core.print

error ErrorSet:
	Value1, Value2;

def main():
	print("Hello, World!\n");

This program will just print this line to the console:

Hello, World

Throwing errors

But here you can directly see how to define a new error set. We use the error keyword for that. And an error set looks pretty similar to an enum defiition, actually, but they are vastly different from one another. Let's look into how to throw an eror next.

use Core.print

error ErrorSet:
	Value1, Value2;

def fail():
	throw ErrorSet.Value1;

def main():
	fail();
	print("Normal execution continues\n");

This program will print these lines to the console:

ERROR: main function returned error
 - type_id: 4008229038
 - value_id: 0
 - message: ""

This error message will change in the future

In the future the message should look more like

ERROR: main function returned error
 - ErrorSet.Value1: ""

but it will take more time and developement effort until this message can actually be printed. It will take a while until we can print the error set names and values because we first need a way to create hash-tables to map the type ids to the type names. So, this will be implemented eventually.

Catching errors

But what would an error be used for if we would not have a way to catch it? Here is how we catch errors in Flint:

use Core.print

error ErrorSet:
	Value1, Value2;

def fail():
	throw ErrorSet.Value1;

def main():
	fail() catch err:
		print("Error catched!\n");
	print("Normal execution continues\n");

This program will print these lines to the console:

Error catched!
Normal execution continues

The program now does no longer print the ERROR: ... message, as no error was thrown from the main function. This is a core concept of Flint: When an error is catched and it is handled it is consiered that the error is "no longer a problem" and execution will continue as if there was no error at all. But there is still a lot more to cover before we are able to completely understand what errors really are and how to effectively use them.