Fly

A programming language for writing easily and quickly all types of software.

main.fly
import fly.os.io.*

void main() {
    print("Hello, World!")
}

Fast

Built on LLVM, Fly compiles to optimised native code for all major platforms.

✍️

Simple

Clean, readable syntax designed to be easy to write and understand with no unnecessary complexity.

🛡️

Powerful

Multi-paradigm with procedural, object-oriented, and functional programming support.

Return values without the copy

Declare a return type before the function name. Inside the body, assign to out — the implicit return variable.

In C++ you must choose: File f = open(path) (readable, but implies a copy) or open(path, &f) (efficient, but noisy). Fly does both with the same line — the source reads as return-by-value, the compiler generates a hidden pass-by-reference.

Same LLVM output. Zero extra cost. No compromise on readability.

Read the Docs
// return type declared in the signature
int calc(const int n) {
    out = n * 2   // 'out' is the implicit return variable
}

void main() {
    int x = calc(21)   // x = 42 — ABI is hidden pass-by-reference
}

Error handling, rethought

Use fail to signal an error with an optional code and message. Errors propagate automatically to the caller — no explicit rethrow needed.

Wrap calls in handle to capture any error into the error variable, populated automatically. Then branch on it to decide what to do.

Read the Docs
get(const string url) {
    if (url == "") {
        fail 1, "url is empty"
    }
}

void main() {
    handle {
        get("")
    }
    if (error) {
        // manage error ...
    }
}

A clean object model

Fly has three distinct building blocks: struct for pure data, class for objects with behaviour, and interface for contracts.

A struct holds fields and can extend one other struct — no virtual dispatch, no hidden cost.
A class adds virtual methods via vtable and can extend a struct and implement multiple interfaces.
An interface defines method signatures only and can extend multiple other interfaces.

Plain new on a struct allocates on the stack — freed automatically, no delete needed.
Plain new on a class allocates on the heap — freed with delete, or use new unique / new shared to let the compiler handle it.

Read the Docs
struct Point {
    int x
    int y
}

interface Drawable {
    draw()
}

class Circle : Point, Drawable {
    int radius
    draw() { /* render ... */ }
}

void main() {
    Circle c = new Circle()
    c.radius = 5
    c.draw()
    delete c
}
process() {
    Point p = new unique Point()
    p.x = 10
    p.y = 42
}   // ← p freed automatically here

void main() {
    process()   // no leaks, no delete
}

Memory management, your way

Fly gives you three ownership models — no garbage collector, no runtime overhead.

new unique grants exclusive ownership: the object is freed automatically when the variable goes out of scope.
new shared uses reference counting: the object lives as long as at least one owner exists.
new weak leaves lifetime control to you, with no counting overhead.

All three are deterministic: memory is released exactly when the scope ends, with no pauses and no surprises.

Read the Docs

Ready to fly?

Install Fly and start building today.

Install Fly