Fifth is a systems programming language with first-class support for knowledge graphs and semantic web technologies. It combines imperative programming with RDF triple management.
// This is a single-line comment
/*
Multi-line comments
work like this
*/
//////////////////////////////////////
// 1. Basic Syntax and Literals
//////////////////////////////////////
// Every Fifth program needs a main function with an int return type
main(): int {
return 0;
}
// Integer literals support multiple bases
main(): int {
x: int;
x = 42; // Decimal
x = 0b101010; // Binary (prefix 0b)
x = 0o52; // Octal (prefix 0o)
x = 0x2A; // Hexadecimal (prefix 0x)
x = 42i; // Imaginary numbers
return 0;
}
// Floating-point literals
main(): int {
pi: float;
pi = 3.14159;
pi = 3.14e0; // Scientific notation
pi = 0x1.921fb54442d18p+1; // Hex float
return 0;
}
// Boolean literals
main(): int {
t: bool;
f: bool;
t = true;
f = false;
return 0;
}
// String literals
main(): int {
plain: string;
raw: string;
interpolated: string;
plain = "Hello, World!"; // Interpreted strings
raw = `Raw\nstring\twith\escapes`; // Raw strings (backticks)
interpolated = $"Value is {x}"; // Interpolated strings ($ prefix)
return 0;
}
// Rune literals (single characters)
main(): int {
c: rune;
c = 'A';
c = '\n'; // Escape sequences
c = '\u0041'; // Unicode escape
return 0;
}
// Null literal
main(): int {
ptr: int;
ptr = null;
return 0;
}
//////////////////////////////////////
// 2. Variables and Type Declarations
//////////////////////////////////////
// Variable declarations use colon syntax: name : type
main(): int {
x: int;
y: float;
s: string;
// Declaration with initialization
z: int;
z = 100;
return 0;
}
// Type specifications for arrays and lists
main(): int {
arr: int[10]; // Fixed-size array
dynamicArr: int[]; // Dynamic array
matrix: int[5][5]; // Multi-dimensional array
// List type with brackets
numbers: [int];
// Generic types
optional: Maybe<int>;
return 0;
}
//////////////////////////////////////
// 3. Operators
//////////////////////////////////////
// Arithmetic operators
main(): int {
x: int;
x = 10 + 5; // Addition
x = 10 - 5; // Subtraction
x = 10 * 5; // Multiplication
x = 10 / 5; // Division
x = 10 % 3; // Modulo
x = 2 ** 8; // Power (exponentiation with **)
x = 2 ^ 8; // Bitwise XOR (use ** for power)
return 0;
}
// Comparison operators
main(): int {
result: bool;
result = 5 == 5; // Equality
result = 5 != 3; // Inequality
result = 5 < 10; // Less than
result = 5 <= 5; // Less than or equal
result = 10 > 5; // Greater than
result = 10 >= 5; // Greater than or equal
return 0;
}
// Logical operators
main(): int {
result: bool;
result = true && false; // Logical AND
result = true || false; // Logical OR
result = !true; // Logical NOT
result = true !& false; // Logical NAND
result = true !| false; // Logical NOR
result = true ~ false; // Logical XOR
return 0;
}
// Bitwise operators
main(): int {
x: int;
x = 5 | 3; // Bitwise OR
x = 5 & 3; // Bitwise AND
x = 5 << 2; // Left shift
x = 20 >> 2; // Right shift
return 0;
}
// Increment and decrement
main(): int {
x: int;
x = 5;
x++; // Post-increment
x--; // Post-decrement
++x; // Pre-increment
--x; // Pre-decrement
return 0;
}
// Compound assignment
main(): int {
x: int;
x = 10;
x += 5; // x = x + 5
x -= 3; // x = x - 3
return 0;
}
//////////////////////////////////////
// 4. Functions
//////////////////////////////////////
// Function declaration syntax: name(params): returnType { body }
add(x: int, y: int): int {
return x + y;
}
// Multiple parameters
greet(firstName: string, lastName: string): string {
return firstName;
}
// Functions are called before main is defined
main(): int {
sum: int;
sum = add(5, 3);
return 0;
}
// Function with parameter constraints
// Use pipe | to specify constraints on parameters
// IMPORTANT: When using constraints, you must provide a base case
// The base case is an unconstrained version that handles all other inputs
positive(x: int | x > 0): int {
return x * 2; // Handles positive numbers
}
positive(x: int): int {
return 0; // Base case: handles zero and negative numbers
}
// Multiple constrained overloads with a base case
classify(x: int | x < 0): string {
return "negative";
}
classify(x: int | x == 0): string {
return "zero";
}
classify(x: int | x > 0): string {
return "positive";
}
classify(x: int): string {
return "unknown"; // Base case (fallback)
}
callClassify(): int {
return 0;
}
// Parameter destructuring
class Person {
FirstName: string;
LastName: string;
}
greetPerson(p: Person { first: FirstName, last: LastName }): string {
return first;
}
testGreet(): int {
return 0;
}
//////////////////////////////////////
// 5. Control Flow
//////////////////////////////////////
// If statements
main(): int {
x: int;
x = 10;
if (x > 5) {
x = 1;
}
// If-else
if (x < 5) {
x = 0;
} else {
x = 1;
}
return 0;
}
// While loops
main(): int {
i: int;
i = 0;
while (i < 10) {
i++;
}
return 0;
}
// With statement (scoped resource management)
main(): int {
with resource {
;
}
return 0;
}
// Try/catch/finally for exception handling
main(): int {
result: int;
result = 0;
// Try with finally - finally always executes
try {
result = 10;
} finally {
std.print("cleanup");
}
// Try/catch - handles exceptions
try {
result = 42;
} catch {
result = 1; // Catch-all handler
}
// Try/catch/finally combined
try {
result = 10;
} catch {
result = 1;
} finally {
result = result + 5; // Always executes
}
return result;
}
//////////////////////////////////////
// 6. Lists and Comprehensions
//////////////////////////////////////
// List literals
main(): int {
empty: [int];
numbers: [int];
empty = [];
numbers = [1, 2, 3, 4, 5];
return 0;
}
// List comprehensions with filtering
main(): int {
xs: [int];
ys: [int];
xs = [1, 2, 3, 4, 5];
// List comprehension: [var in source # constraint]
ys = [x in xs # x > 2]; // [3, 4, 5]
return 0;
}
// Accessing list elements
main(): int {
numbers: [int];
first: int;
numbers = [10, 20, 30];
first = numbers[0]; // Index access
return 0;
}
//////////////////////////////////////
// 7. Classes and Objects
//////////////////////////////////////
// Class definition
class Rectangle {
Width: float;
Height: float;
}
// Class with methods
class Calculator {
Value: int;
Add(x: int): int {
return Value + x;
}
Multiply(x: int): int {
return Value * x;
}
}
// Class instantiation
main(): int {
rect: Rectangle;
calc: Calculator;
// Create new object (can be empty)
rect = new Rectangle();
// Create with property initialization
rect = new Rectangle() {
Width = 10.0,
Height = 5.0
};
calc = new Calculator() { Value = 100 };
return 0;
}
// Class inheritance
class Shape {
Color: string;
}
class Circle extends Shape {
Radius: float;
}
main(): int {
c: Circle;
c = new Circle();
return 0;
}
// Member access
main(): int {
rect: Rectangle;
w: float;
rect = new Rectangle() { Width = 10.0, Height = 5.0 };
w = rect.Width; // Access member
rect.Width = 15.0; // Modify member
return 0;
}
//////////////////////////////////////
// 8. Generic Types
//////////////////////////////////////
// Generic classes allow type parameters for reusable data structures
// Syntax: class Name<TypeParam> { ... }
class Stack<T> {
items: [T];
push(item: T): int {
return 0;
}
pop(): T {
return items;
}
}
main(): int {
intStack: Stack<int>;
stringStack: Stack<string>;
// Each instantiation creates a distinct type
intStack = new Stack<int>();
stringStack = new Stack<string>();
return 0;
}
// Multiple type parameters
class Pair<T1, T2> {
first: T1;
second: T2;
}
class Dictionary<TKey, TValue> {
keys: [TKey];
values: [TValue];
}
main(): int {
pair: Pair<int, string>;
dict: Dictionary<string, int>;
pair = new Pair<int, string>() {
first = 42,
second = "answer"
};
return 0;
}
// Generic functions with type inference
// The compiler can infer type arguments from the call site
identity<T>(x: T): T {
return x;
}
main(): int {
result: int;
text: string;
// Explicit type arguments
result = identity<int>(42);
text = identity<string>("hello");
// Type inference (types inferred from arguments)
result = identity(42); // Infers T = int
text = identity("world"); // Infers T = string
return 0;
}
// Functions with multiple type parameters
pair<T1, T2>(a: T1, b: T2): int {
return 0;
}
triple<T1, T2, T3>(a: T1, b: T2, c: T3): int {
return 0;
}
main(): int {
// Type inference works with multiple parameters
pair(1, "one"); // Infers T1=int, T2=string
triple(1, 2.0, "three"); // Infers T1=int, T2=float, T3=string
return 0;
}
// Type constraints ensure type parameters meet requirements
// Syntax: where TypeParam: Constraint
// Interface constraint
sort<T>(items: int): int where T: IComparable {
return 0;
}
// Base class constraint
extend<T>(base: T): int where T: BaseClass {
return 0;
}
// Multiple constraints
process<T>(item: T): int where T: IComparable, IDisposable {
return 0;
}
// Constraints on multiple type parameters
class Mapper<TIn, TOut> where TIn: IComparable where TOut: BaseType {
input: TIn;
output: TOut;
}
main(): int {
mapper: Mapper<int, string>;
return 0;
}
// Generic methods in classes
class Container<T> {
value: T;
// Non-generic method using class type parameter
getValue(): T {
return value;
}
// Methods can have their own type parameters
// (Note: Parser limitations exist for method-level generics)
}
class Util {
// Generic methods in non-generic classes
swap(x: int, y: int): int {
return 0;
}
}
main(): int {
container: Container<int>;
util: Util;
container = new Container<int>() { value = 42 };
util = new Util();
return 0;
}
// Nested generic types
class Box<T> {
items: [T]; // List of T
}
main(): int {
// Box containing a list of integers
intBox: Box<int>;
// You can nest generic types
// (Advanced nested syntax may have parser limitations)
return 0;
}
// Key Points about Generics:
// 1. Full type reification: Stack<int> and Stack<string> are distinct types at runtime
// 2. Type inference works from function call arguments (local inference, C#-style)
// 3. Type parameters can have constraints (interface, base class)
// 4. Multiple type parameters supported: Pair<T1, T2>, Dictionary<TKey, TValue>
// 5. Generic methods inherit functionality from class type parameters
// 6. Backward compatible: all non-generic code works unchanged
//////////////////////////////////////
// 8. Module System
//////////////////////////////////////
// Import modules
use Math, IO, Net;
// Multiple imports
use System, Collections;
main(): int {
return 0;
}
//////////////////////////////////////
// 9. Knowledge Graphs & Semantic Web
//////////////////////////////////////
// Alias declarations for IRI namespaces
alias ex as <http://example.org/>;
alias foaf as <http://xmlns.com/foaf/0.1/>;
// Store declaration (SPARQL endpoint)
// Syntax: name : store = sparql_store(<iri>);
myStore : store = sparql_store(<http://localhost:8080/graphdb>);
// Triple literals: <subject, predicate, object>
main(): int {
// Triples use prefixed IRIs
<ex:john, foaf:name, "John Doe">;
<ex:john, foaf:age, 30>;
<ex:john, ex:knows, ex:jane>;
return 0;
}
// Graph declaration
main(): int {
// Graph variable with colon syntax
g : graph in <ex:> = KG.CreateGraph();
// Add triples to the graph
g += <ex:subject1, ex:predicate1, ex:object1>;
g += <ex:subject2, ex:predicate2, 42>;
return 0;
}
// Working with graphs
main(): int {
g : graph = KG.CreateGraph();
g += <ex:s, ex:p, ex:o>;
return 0;
}
// Working with graphs and objects
class Person {
Name: string;
Age: int;
}
main(): int {
// Create an object
alice: Person;
alice = new Person();
// Create graph and add triples
peopleGraph : graph in <ex:> = KG.CreateGraph();
// Add explicit triple literals
peopleGraph += <ex:alice, ex:name, "Alice">;
peopleGraph += <ex:alice, ex:age, 30>;
peopleGraph += <ex:alice, ex:knows, ex:bob>;
return 0;
}
// Assigning graphs to stores
alias ex as <http://example.org/>;
myStore : store = sparql_store(<http://localhost:8080/graphdb>);
main(): int {
myGraph : graph in <ex:> = KG.CreateGraph();
myGraph += <ex:entity, ex:property, "value">;
// Add graph to store
myStore += myGraph;
// Remove graph from store
myStore -= myGraph;
return 0;
}
// Classes in semantic context
class Entity in <http://example.org/ontology#> {
Name: string;
Value: int;
}
main(): int {
e: Entity;
e = new Entity() { Name = "Test", Value = 42 };
return 0;
}
//////////////////////////////////////
// 10. Advanced Features
//////////////////////////////////////
// Nested destructuring
class Address {
Street: string;
City: string;
}
class Employee {
Name: string;
HomeAddress: Address;
}
processEmployee(
e: Employee {
name: Name,
addr: HomeAddress { city: City }
}
): string {
return City;
}
main(): int {
return 0;
}
// Function with multiple constraints
// All constrained overloads require a base case
constrained(
x: int | x > 0,
y: int | y < 100
): int {
return x + y; // Handles when x>0 AND y<100
}
constrained(x: int, y: int): int {
return 0; // Base case: handles all other combinations
}
main(): int {
result: int;
result = constrained(5, 50);
return 0;
}
// Expression statements
main(): int {
x: int;
// Any expression can be a statement
5 + 3;
x = 10;
x > 5;
return 0;
}
// Block statements
main(): int {
x: int;
{
y: int;
y = 5;
} // y goes out of scope
return 0;
}
//////////////////////////////////////
// 11. Full Example: Semantic Application
//////////////////////////////////////
// Define namespace aliases
alias foaf as <http://xmlns.com/foaf/0.1/>;
alias ex as <http://example.org/people/>;
// Connect to a SPARQL store
peopleDB : store = sparql_store(<http://localhost:8080/graphdb>);
// Define a class for people
class Person {
FirstName: string;
LastName: string;
Age: int;
}
// Function to calculate if someone is an adult
isAdult(age: int): bool {
return age >= 18;
}
// Main program
main(): int {
// Create a person
john: Person;
john = new Person() {
FirstName = "John",
LastName = "Doe",
Age = 30
};
// Check if adult
adult: bool;
adult = isAdult(john.Age);
// Create knowledge graph and add triples
johnGraph : graph in <ex:> = KG.CreateGraph();
// Add triples for John's properties
johnGraph += <ex:john, foaf:firstName, "John">;
johnGraph += <ex:john, foaf:lastName, "Doe">;
johnGraph += <ex:john, foaf:age, 30>;
// Save to the store
peopleDB += johnGraph;
return 0;
}