Skip to main content

Types

Every column in a .decl gets a type. FlowLog is statically typed — the compiler catches type mismatches before anything runs.

Numeric types

Signed integers

Your everyday integers. Pick the smallest size that fits your data:

TypeRust equivalentRange
int8i8-128 to 127
int16i16-32 768 to 32 767
int32i32-2.1 × 109 to 2.1 × 109
int64i64-9.2 × 1018 to 9.2 × 1018
.decl Score(student_id: int32, grade: int32)

Unsigned integers

When you know values are never negative:

TypeRust equivalentRange
uint8u80 to 255
uint16u160 to 65 535
uint32u320 to 4.3 × 109
uint64u640 to 1.8 × 1019
.decl Pixel(x: uint16, y: uint16, color: uint32)

Floats

Floating-point numbers, wrapped in OrderedFloat under the hood so they can be hashed and compared correctly (no NaN surprises):

TypeRust equivalentPrecision
f32f32~7 decimal digits
f64f64~15 decimal digits
.decl Measurement(sensor: int32, value: f64)

String

The string type holds UTF-8 text. String constants use double quotes:

.decl Name(id: int32, label: string)
Name(1, "alice").

Boolean

The bool type is True or False — capitalized. Lowercase true won't work:

.decl Flag(id: int32, active: bool)
Flag(1, True).

Using types in declarations

Every column needs an explicit type — no inference, no defaults:

.decl Edge(src: int32, dst: int32)
.decl Person(id: int64, name: string, active: bool)
.decl Sensor(id: uint32, reading: f64, label: string)

Type checking

FlowLog catches type errors at compile time in these cases:

Joins

When a variable appears in multiple atoms, its type must agree everywhere. If Edge.dst is int32 and Node.id is int64, joining on the same variable is a type error:

.decl Edge(src: int32, dst: int32)
.decl Node(id: int32, label: string)

// Works: y is int32 in both Edge and Node
Match(x, label) :- Edge(x, y), Node(y, label).

Change Node.id to int64 and the compiler will complain — y can't be two types at once.

Floats work as join keys too, thanks to OrderedFloat:

.decl A(x: f32, y: f32)
.decl B(x: f32, z: f64)

// x is f32 in both — valid join
AB(x, y, z) :- A(x, y), B(x, z).

Rule heads

All rules deriving the same relation must produce matching types:

.decl Route(src: int32, dst: int32, cost: int32)

// Both rules produce (int32, int32, int32) — good
Route(x, y, c) :- Edge(x, y), Weight(x, y, c).
Route(x, z, c) :- Route(x, y, _), Edge(y, z), Weight(y, z, c).

What FlowLog does not check

Integer and float constant overflow (e.g., stuffing 999 into a uint8) and comparison type mismatches (e.g., int32 > int64) are not caught by FlowLog. The downstream Rust compiler will catch these for you — so you'll still get an error, just a less friendly one.