Essential patterns for Hexen's comptime type system
Key Insight: Literals stay flexible until context forces them to become concrete.
42 // comptime_int (flexible!)
3.14 // comptime_float (flexible!)
val x = 42 // Still comptime_int (preserved!)
val y : i32 = 42 // NOW becomes i32 (context forces resolution)
| Category | Examples | Behavior |
|---|---|---|
| Comptime | 42, 3.14, val x = 42 |
Adapts to context seamlessly |
| Concrete | val x : i32 = 42, function results |
Requires explicit conversions |
| Mixed | comptime + concrete |
Comptime adapts, result is concrete |
Is it a literal (42, 3.14)?
├─ YES → comptime type (flexible)
└─ NO → Is it from a function call or explicitly typed?
├─ YES → concrete type (fixed)
└─ NO → Is it arithmetic between comptime types only?
├─ YES → comptime type (flexible)
└─ NO → concrete type (fixed)
val math = 42 + 100 * 3.14 // comptime_float (stays flexible!)
val as_f32 : f32 = math // Same source → f32
val as_f64 : f64 = math // Same source → f64
val as_i32 : i32 = math:i32 // Same source → i32 (explicit conversion)
val count : i32 = 100
val result : i32 = count + 42 // i32 + comptime_int → i32 (adapts)
val bigger : i64 = count + 42 // i32 + comptime_int → i64 (context guides)
val a : i32 = 10
val b : i64 = 20
// val mixed = a + b // ❌ Error: mixed concrete types
// val result : i64 = a + b // ❌ Still error: context doesn't help with mixed concrete
val explicit : i64 = a:i64 + b // ✅ Explicit conversion required
val a : i32 = 10
val b : i32 = 20
val result : i32 = a + b // i32 + i32 → i32 (no conversion)
| Context Source | Example | Effect |
|---|---|---|
| Type Annotation | val x : i64 = 42 |
comptime_int → i64 |
| Function Parameter | process(42) where process(x: i32) |
comptime_int → i32 |
| Function Return | return 42 in func() : f32 |
comptime_int → f32 |
| Assignment Target | counter = 42 where counter : i32 |
comptime_int → i32 |
| Mixed Operation | concrete_var + 42 |
comptime_int → concrete_var's type |
// ✅ val preserves comptime types (maximum flexibility)
val flexible = 42 + 100 // comptime_int (flexible!)
val as_i32 : i32 = flexible // Can adapt to i32
val as_f64 : f64 = flexible // Same source adapts to f64
// 🔒 mut requires explicit types (safety over flexibility)
mut counter : i32 = 42 + 100 // Must specify type, becomes concrete i32
// val cant_adapt : i64 = counter // ❌ No flexibility left
val converted : i64 = counter:i64 // ✅ Explicit conversion required
Pattern: value:target_type
val widened : i64 = i32_val:i64 // i32 → i64
val narrowed : i32 = i64_val:i32 // i64 → i32 (data loss visible)
val as_float : f64 = int_val:f64 // i32 → f64
val truncated : i32 = float_val:i32 // f64 → i32 (truncation visible)
val x = 42 // This is comptime_int, not i32!
val y = 3.14 // This is comptime_float, not f64!
val a : i32 = 10
val b : i64 = 20
// val result = a + b // ❌ Error: mixed concrete types need explicit conversion
// val result : i64 = a + b // ❌ Still error: context doesn't help
val result : i64 = a:i64 + b // ✅ Explicit conversion required
val concrete : i32 = compute_value()
// val flexible = concrete // ❌ No, concrete stays concrete
val CONFIG_SIZE = 1024 * 1024 // comptime_int (flexible!)
val small_buffer : i32 = CONFIG_SIZE // Adapts to i32
val large_buffer : i64 = CONFIG_SIZE // Same constant, different type
val int_val : i32 = 42
val float_val : f64 = 3.14
// val result : f64 = int_val + float_val // ❌ Error: mixed concrete types
val result : f64 = int_val:f64 + float_val // ✅ Explicit conversion required
val math_expr = 42 * 3.14 + 100 // comptime_float (flexible!)
// Use in different contexts later...
val precise : f64 = math_expr // High precision
val fast : f32 = math_expr // Fast computation
- Start Simple: Use literals with explicit types (
val x : i32 = 42) - Learn Flexibility: Try
val x = 42and use in different contexts - Understand Boundaries: Learn when comptime becomes concrete
- Master Conversions: Practice explicit conversion syntax
- Apply Patterns: Use the four patterns in real code
- Check if types are comptime or concrete:
val x = 42vsval x : i32 = 42 - Look for resolution boundaries: Function calls, explicit types, mixed operations
- Add explicit context: Use
: typeannotations to guide resolution - Use explicit conversions: When mixing concrete types, use
value:type
Remember: Comptime types are your friend! They adapt to make your code more flexible while keeping all costs visible. 🦉