Records and Tuples
Records and tuples are fundamental data structures in many languages; in Nanyx they are a single feature. They allow you to group related values together with or without names.
Records
Records are structural types with named fields enclosed in parentheses:
-- Record creation
def user = (
name = "Alice"
age = 30
email = "alice@example.com"
)Record Types
Define record types to describe the shape of your data:
type Person = (
name: string
age: int
email: string
)
-- Using the type
def alice: Person = (
name = "Alice"
age = 30
email = "alice@example.com"
)Accessing Record Fields
Access fields using dot notation:
def user = (name = "Bob", age = 25)
def userName = user.name -- "Bob"
def userAge = user.age -- 25Record Updates (Non-destructive mutation / copy-and-update)
In a language where immutability is the default, it is almost required that there is a way to make a copy of a value with some of its properties changed. This is called "copy-and-update" or "non-destructive mutation". Nanyx has two special syntactical forms for this:
- The
withkeyword - The spread operator
...
The important difference between these two forms is that the with keyword works on exactly one input object, and the result of the with expression will always be the same type as the input object. The spread operator, meanwhile, can be used on any number of input objects and the resulting object may well be a different type from any of the input objects.
def x = (a = 1, b = 2)
def y = x with (a = 3)
def z = x with (d = 4) -- Error: the `with` keyword cannot be used to add new propertiesdef x = (a = 1, b = 2)
def y = (...x, a = 3) -- `y` is now (a = 3, b = 2)
def z = (...x, c = 4) -- `z` is now `(a = 1, b = 2, c = 4)`. Note the fact that a new property has been addedUnlike with, the spread operator can be used on multiple input objects, to create a mix of properties
def x = (a = 1, b = 2)
def y = (c = 3, d = 4)
def z = (...x, ...y) -- `z` is now `(a = 1, b = 2, c = 3, d = 4)`Note that the order of the input objects is important. If two input objects have the same property, the last one will be used.
Nested Records
Records can contain other records:
type Address = (
street: string
city: string
zipCode: string
)
type Person = (
name: string
age: int
address: Address
)
def person = (
name = "Alice"
age = 30
address = (
street = "123 Main St"
city = "Springfield"
zipCode = "12345"
)
)
-- Access nested fields
def city = person.address.city -- "Springfield"Record Destructuring
Extract fields from records with pattern matching:
def user = (name = "Bob", age = 25)
-- Destructure in definition
def (name = userName, age = userAge) = user
-- Destructure in function parameter
def greet: Person -> string = { (name = n, age = a) ->
"Hello {n}, age {a}"
}
-- Partial destructuring
def (name = name, _) = user -- only extract nameTuples
Tuples are anonymous records with numbered fields:
-- Tuple creation
def point = (10, 20)
-- Access by position
def (x, y) = point
-- Tuple type
def origin: (int, int) = (0, 0)Named Tuples
You can name tuple fields, making them equivalent to records:
-- Named tuple
def coords = (x = 10, y = 20)
-- Access by name
def x = coords.x -- 10
-- Or destructure
def (x = xVal, y = yVal) = coordsRecord Patterns in Matching
Use records in pattern matching:
type Point = (x: int, y: int)
def classify: Point -> string = { point ->
match point
| (x = 0, y = 0) -> "origin"
| (x = 0, y = _) -> "on y-axis"
| (x = _, y = 0) -> "on x-axis"
| (x = x, y = y) if x == y -> "on diagonal"
| _ -> "general point"
}Records as Function Parameters
Records make multi-parameter functions natural:
-- Function taking multiple parameters
def calculateArea: (width: int, height: int) -> int = { width, height ->
width * height
}
-- Call with named arguments
def area = calculateArea(width = 10, height = 20)
-- Or with a record
def dimensions = (width = 10, height = 20)
def area2 = calculateArea(dimensions)Structural Typing
Records use structural typing - they're compatible based on shape, not name:
type Point2D = (x: int, y: int)
type Coord = (x: int, y: int)
-- These are the same type!
def point: Point2D = (x = 10, y = 20)
def coord: Coord = point -- OK!