Introduction to Guardian
Guardian is a compiled, statically typed language designed for parallel computing with the Guard synchronization model.
If you have used another imperative, class-based language, such as Java or C++, you will find many aspects of Guardian familiar.
To begin, let's look at some of the characteristics of Guardian which are not terribly unique:
-
Compiled: Guardian is compiled to native code, which means that it runs faster than interpreted languages.
-
Statically typed: Guardian must know the types of all variables at compile time.
Class fields, as well as function parameters and return values, must be explicitly annotated with a type.
Types are inferred in most other situations, such as local variable declarations and lambda parameters, meaning that
you don't have to write them.
-
Class-based: Guardian organizes code into classes, which are similar to classes in Java or C++.
-
High-level: In Guardian, you don't have to worry about memory management, undefined behavior, keeping track of pointers, or other such nastiness.
Next, let's look at the fundamental principles of Guardian:
-
Safety first: Parallel programming is too hard for mortals already.
- Being first in the list of principles is not an accident.
-
Always asynchronous, never blocking: Blocking is a source of pain.
-
No shared access to mutable state:
- Immutable state may be shared freely.
- Mutable state may be shared between threads if the compiler can prove that it is not accessed concurrently.
- GuardVar protects mutable state.
- A unique reference to mutable state may be "moved" between threads.
- Only one thread at a time is able to access the mutable state.
-
Every task is linearizable.
-
Automate when possible.
- Garbage collection.
- When possible, manage memory statically by use of unique references (some inspiration from Rust) instead of garbage collection.
- Compile-time assurances of progress and termination.
- The entry method returns a Future whose completion indicates that the program may terminate.
- All operations which perform asynchronous work return a Future.
- Futures cannot be discarded: we call this the must-consume rule.
All Futures that represent progress towards the termination condition must be either consumed or passed
along to other parts of the program.
-
Parallel arrays are a primary concern.
-
Prefer compile-time errors to runtime errors.
-
Minimize nesting.
-
Simplicity.
-
Uniformity.
-
Minimize (eliminate) use of atomics.
Hello World
Here is the obligatory Hello World program in Guardian:
namespace hi;
import gu4::Out;
value class Hello {
entry fn main() -> void {
Out::println("Hello, world!");
}
}
Let's pick that apart.
- The first line is a namespace declaration. Namespaces are used to organize code into modules. In Guardian,
all code must be in a namespace, and each file must begin with a namespace declaration.
In this case, the program is in the
hi namespace. Namespaces can also be nested, such as hi::there.
- The next line imports the
Out class from the gu4 namespace. This is used to print text to the terminal.
The gu4 namespace contains many useful classes and functions which comprise the Guardian standard library.
- The next line declares a class named
Hello.
Classes are used to organize code into objects.
In this case, the class contains a single method named main.
- The
main method is the entry point of the program. It is called when the program is run. It has no return value,
so its return type is declared as void.
- The body of the
main method is a single statement, which prints "Hello, world!" to the terminal. Out is a class,
and println is a static method of that class. Were println a non-static method, it would have to be called on an
instance of the Out class using the . dereference operator instead of the :: scope resolution operator.
But what about the value keyword? This denotes the type's sharing policy, which is a part of
Guardian's model for safe concurrency. For now,
all you need to know is that a value class is one that has either immutable state or no state at all
(as in this case), and therefore can be freely shared between threads.
To continue, let's embark on the tour.