Flint logolint
high level power, transparent runtime.

Flint is a statically typed, compiled language designed around transparency. Write expressive, high-level code while understanding exactly what happens at runtime. Automatic memory management through DIMA, powerful composable objects, callables and persistent locals, and interoperability with C libraries make Flint practical for real-world projects.

Flint is in late beta. The language has stabilized with rare breaking changes. Try it out and share feedback!

Why choose Flint?

Flint stands out by combining high-level ergonomics with runtime transparency. This combination is rather uncommon, as most languages either want transparency or high-level ergonomics but rarely both.

Transparent Runtime

Unlike many high-level languages, you can always understand what your code is doing. High-level syntax maps directly to predictable, inspectable behavior you can reason about.

Compositional Core

Flint's core is centered on the composition of components into larger objects. Regarding modern definitions of OOP, Flint is an OOP language, but you will not find anything regarding inheritance in Flint. The compositional design enables you to build complex systems which always stay simple to extend, with clear semantics.

Seamless C Interoperability

The Flint Interop Protocol (FIP) lets you use advanced C libraries like raylib without writing bindings. Use what you need from the C ecosystem effortlessly.

Automatic Memory Management

DIMA (Deterministic Incremental Memory Architecture) provides automatic, garbage collection-free memory management that you can understand and predict.

Static Compilation

Compile to optimized native code. Flint is designed to produce fast, predictable executables that perform well in real-world applications.

Comprehensive Learning Path

The Wiki provides a complete learning journey from beginner to expert. It is always up to date with every feature and rule explained, as Flint aims to be finite.

Composition as a Core Concept

Flint centers itself around composition as a core concept. This enables it to make some static guarantees about the code you write. Because data and functionality is separated and then composed in a declarative manner, the runtime can optimize code in ways it would not be able to with "classic" objects in OOP. This compositional approach requires a different mindset than OOP, you might need to adjust your thinking to use it effectively.

Key Concepts

1
Data Components Define what data your systems contain. Data components are reusable across many different func components and objects.
2
Func Components Define behavior that operates on specific data. Transparent about requirements and effects.
3
Interfaces Define polymorphic behaviour through the use of interfaces. Implement them in your objects as Flint's way to add polymorphism to an otherwise entirely static language.
4
Objects Compose data and behavior together declaratively. Objects aim to feel as similar to "regular" objects in OOP-based languages despite Flint's compositional core.

Composition Example

use Core.print

// Define data
data Wings:
    u32 size;
    u32 flight_time;
    Wings(size, flight_time);

// Define behavior
func Fly requires(Wings w):
    def fly():
        print($"Flying for {w.flight_time} seconds\n");

// Compose into object
object Bird:
    data: Wings;
    func: Fly;
    Bird(Wings);

def main():
    bird := Bird(Wings(10, 20));
    bird.fly();

Seamless C Interoperability

The FIP (Flint Interop Protocol) is a protocol-based communication library aiming to unify interop between languages. The Flint compiler communicates with specialized Interop Modules over this protocol, to enable native interop with other (compiled) languages. The C interop module is the only one working so far, but more will follow.

Small Raylib Example

// Include auto-generated FIP module aliased as 'rl'
use Fip.raylib as rl

// Const data is comptime-substituted
const data Col:
    u8x4 bg = u8x4(245, 245, 245, 255);
    u8x4 fg = u8x4(45, 45, 45, 255);

def main():
    rl.InitWindow(1280, 720, "Raylib Example");
    while not rl.WindowShouldClose():
        rl.BeginDrawing();
        rl.ClearBackground(Col.bg);
        rl.DrawText("Hello from Raylib!", 100, 200, 60, Col.fg);
        rl.EndDrawing();
    rl.CloseWindow();

Key Concepts

1
Protocol-Based Communication Each Interop Module is a master in its own language, the Flint compiler does not need to know about other languages at all.
2
Interop Compile Errors Through FIP the compiler can detect which functions or symbols are missing or nonexistent at parse-time. No more hard to diagnose "symbol not found" linking errors!
3
Auto-Generated Bindings The compiler auto-generates bindings for code from supported foreign languages for you.

Callables and Variable Persistence

In Flint, every function can be referenced as a callable and passed around as a value. By referencing functions as callables, its persistent local state is preserved across invocations. Through this, callables enable powerful patterns like counters, rate limiters, state machines, accumulators, and more. This all is possible thanks to Flint's runtime. How everything shown here works under the hood is explained in-detail in the Wiki.

Key Concepts

1
Functions as Callables When referencing a function, a callable instance is created. Callables are first-class values that can be passed around and invoked like regular functions.
2
Persistent Local State Callables preserve their persistent local state across invocations, allowing for stateful behavior without global variables. This local state is stored on a per-instance basis, so each callable instance has its own local state which is not shared with any other instance.
3
Regular Functions Persistent locals are just regular local variables for regular function calls and can be used as such. Flint does not differentiate between callables and regular functions at the lowest level, the same code is executed for both. This means that every user-defined function is allowed to be referenced as a callable.
4
It's not static You may know the static keyword in languages like C++ or Java, persistent local variables are not the same. Persistent locals are not global variables, they exist on a per-instance basis.

Rate Limiter Example

use Core.print
use Core.time

def execute_function():
    print("Executed\n");

def execute_maybe(fn<> function) -> bool:
    persistent TimeStamp last = now();
    TimeStamp current = now();
    Duration elapsed = duration(last, current);

    if as_unit(elapsed, TimeUnit.S) < 1.0:
        return false;

    function();
    last = current;
    return true;

def main():
    fn<> task = ::execute_function;
    fn<fn<> -> bool throttled = ::execute_maybe;
    i32 c = 0;
    while c < 4:
        if throttled(task):
            c++;

Getting Started

Ready to explore Flint? Start with the comprehensive Wiki that teaches you everything from basics to advanced features.

Read the Wiki

Follow the structured learning guides from beginner to expert. Learn the language fundamentals, core concepts, and advanced features.

Build & Compile

Install Flint and start compiling programs. The compiler is written in C++ and is ready for real-world use in late beta.

Join the Community

Connect with other Flint developers on Discord. Share ideas, ask questions, and contribute to shaping the future of the language.