Products
Ore has one structural product surface with two shapes: positional products and named products. They are distinct types and do not unify with each other.
Positional Products
Positional products use .{ ... } with numeric field access.
t := .{ 10, 20, true }
t.0
t.1
t.2
Type annotation: .{ i32, i32, bool }.
Parentheses are only for grouping. Unit remains ().
(42) // grouping
.{ 42 } // 1-element positional product
() // unit
Named Structural Products
Named products are structural values with labeled fields.
point := .{ .x = 10, .y = 20 }
point.x
point.y
Their type syntax uses the same field set with : instead of =:
.{ .x: i32, .y: i32 }
Type Annotation
alloc :: fn(n: i32) -> .{ .ptr: [*]u8, .len: usize }
.{ .ptr = ore_alloc(n), .len = n as usize }
Field Access
Named products are accessed only by field name.
buf := alloc(100)
buf.ptr
buf.len
As Function Parameters
area :: fn(rect: .{ .w: i32, .h: i32 }) -> i32
rect.w * rect.h
area(.{ .w = 10, .h = 5 })
Key Rules
Field names are part of identity
.{ .x: i32, .y: i32 } and .{ .a: i32, .b: i32 } are different types.
Named and positional products are distinct
.{ .x: i32, .y: i32 } does not unify with .{ i32, i32 }.
Field access matches the product family
Positional products use .0, .1, and so on. Named products use .field.
.{ ... } is structural by default
Without an explicit head, .{ ... } builds a structural product. Use a head when you want a nominal data value or an array.
Point.{ .x = 10, .y = 20 }
[3; i32].{ 1, 2, 3 }
Comparison with data
| Feature | Positional Product | Named Product | data |
|---|---|---|---|
| Identity | Structural by position | Structural by field names | Nominal |
| Access | .0, .1 | .name | .name |
| Type syntax | .{ A, B } | .{ .x: A, .y: B } | Name |
| Literal syntax | .{ 1, 2 } | .{ .x = 1, .y = 2 } | Point.{ .x = 1, .y = 2 } |
Use named structural products for lightweight labeled data. Use data when the type’s name should matter across the program.
Library Convention
Functions that allocate often return a named product containing the owner and its size:
read_file :: fn(path: []const u8) -> .{ .data: [*]u8, .size: i32 }
file := read_file("input.txt")
view := file.data[0..file.size as usize]