Data Types & Formulas
Flame is statically typed with strong type inference. Types can either be explicitly declared or deduced automatically by the compiler.
Primitive Types
Section titled “Primitive Types”| Type | Description | Example |
|---|---|---|
Int |
64-bit signed integer | let age: Int = 28 |
Float |
64-bit IEEE 754 floating-point number | let pi: Float = 3.14159 |
String |
UTF-8 encoded text string | let name: String = "Flame" |
Bool |
Boolean value (true or false) |
let is_ready: Bool = true |
Unknown |
A dynamically resolved type when inference is unavailable | let data: Unknown = fetch() |
Nil |
Represents the absence of a value (void / null) | let empty = nil |
String Interpolation
Section titled “String Interpolation”Flame supports expressive string interpolation using the $ prefix:
let user = "Alice"let score = 98let message = $"Player {user} scored {score} points!"print(message)The Unknown Type
Section titled “The Unknown Type”Flame is statically typed, but there are situations where the compiler cannot definitively infer a variable’s type at compile time—especially when parsing untyped JSON, working with untyped dynamic arrays (like [] without a type annotation), or interfacing with certain dynamic APIs.
In these cases, Flame uses the Unknown type.
Unknownacts as a dynamic type fallback.- You can store any value inside an
Unknownvariable. - Operations on
Unknownvalues bypass strict static type checking and are evaluated dynamically at runtime.
// An empty array has no inferred type, so it becomes [Unknown]let mut data = []data.push(42)data.push("String") // Allowed, because the array holds Unknown typesComposite Types
Section titled “Composite Types”Dynamic Vectors (Vec<T>)
Section titled “Dynamic Vectors (Vec<T>)”A dynamically sized collection of elements sharing type T.
let mut numbers: Vec<Int> = [10, 20, 30]
// Common vector methodsnumbers.push(40)let last = numbers.pop() // 40let length = numbers.len() // 3
// Functional transformationslet doubled = numbers.map((x: Int) { return x * 2 })let filtered = numbers.filter((x: Int) { return x > 15 })
print(doubled) // [20, 40, 60]print(filtered) // [20, 30]Tuples
Section titled “Tuples”Ordered collections of fixed size that can contain distinct types:
let point: (Int, Int) = (100, 250)let record: (String, Int, Bool) = ("Server", 8080, true)
// Access tuple memberslet host = record.0let port = record.1Complex Tuples & Data Extraction
Section titled “Complex Tuples & Data Extraction”Flame relies heavily on Tuples for returning multiple values efficiently without needing boilerplate classes or formulas. Let’s look at a complex function signature as an example:
fn process_events(events: [(Int, Int, Int, Float, Int)]) -> (Int, Float, Float, Float, [Float], Int, [(Float, Int)])The Input: events: [(Int, Int, Int, Float, Int)]
This signifies that events is a dynamically sized Vector ([...]) containing strict Tuples ((...)). Each tuple is exactly 5 elements long. When iterating, we can extract all 5 elements instantly using Tuple Destructuring:
for event in events { let (event_id, timestamp, user_id, value, category) = event // ...}The Output: -> (Int, Float, Float, Float, [Float], Int, [(Float, Int)])
This is a massive return tuple containing primitive stats, an array of floats, an integer, and an array of smaller 2-element tuples. The caller can cleanly extract all 7 distinct items simultaneously using destructuring:
let result = process_events(events)let (count, total, minimum, maximum, category_totals, checksum, processed) = resultStandard Types
Section titled “Standard Types”Flame includes several built-in types to handle common language patterns like error handling and optional values robustly, mimicking modern system languages.
Result<T, E>
Section titled “Result<T, E>”Result is an enum used for returning and propagating errors. It has two variants:
Ok(value): Indicates a successful execution and contains the success value.Err(error): Indicates a failure and contains the error value.
fn divide(a: Int, b: Int) -> Result<Int, Error> { if b == 0 { return Err(Error { message: "Division by zero", code: 1 }) } return Ok(a / b)}
let res = divide(10, 2)Option<T>
Section titled “Option<T>”Option is an enum used when a value might be absent. It has two variants:
Some(value): Contains the value.None: Indicates the absence of a value.
let user_id: Option<Int> = Some(10)let missing_id: Option<Int> = NoneError is a standard built-in struct containing structured information about a failure.
let err = Error { message: "File not found", code: 404}Object vs Formula Literals
Section titled “Object vs Formula Literals”Flame provides two dynamic, map-like data structures: Objects and Formulas. While they share similar runtime semantics, they have distinct use cases and syntax.
Objects ({ ... })
Section titled “Objects ({ ... })”Objects use standard curly braces and are the preferred syntax for general-purpose dynamic records, data destructuring, and JSON payloads.
let user: Object = { name: "Soham", stats: { age: 17 }}
// Object destructuring is fully supportedlet { name, stats } = userprint($"User {name} is {stats.age} years old.")Formulas (formula { ... })
Section titled “Formulas (formula { ... })”The formula literal is a specialized keyword-prefixed structure. It behaves identically to Objects but is intended for use in places where explicit disambiguation is required, such as within annotation payloads.
let config = formula { host: "127.0.0.1", port: 3000, // Duplicate keys overwrite seamlessly: port: 8080, // They can store closures/anonymous functions: on_connect: () { print("Connected!") }}Formulas with Annotations
Section titled “Formulas with Annotations”Formulas are primarily used as metadata payloads for custom annotations where { ... } might conflict with block syntax:
@Entity(formula { table: "users", cache_ttl: 3600 })struct UserRecord { id: Int, name: String}