Data Types ​
Kettle has a rich set of data types for structuring your programs.
Records ​
Records are named collections of fields:
type Point = record
x: Float
y: Float
end record
type Person = record
name: String
age: Int
end recordCreating Records ​
origin = Point { x: 0.0, y: 0.0 }
alice = Person { name: "Alice", age: 30 }Accessing Fields ​
Use dot notation:
print(alice.name) -- "Alice"
print(to_string(origin.x)) -- "0.0"Updating Records ​
Create a new record with some fields changed:
moved = Point { x: origin.x + 1.0, y: origin.y }Variants ​
Variants (also called sum types or tagged unions) represent values that can be one of several forms:
type Shape = variant
Circle(radius: Float)
Rectangle(width: Float, height: Float)
Triangle(base: Float, height: Float)
end variantCreating Variants ​
c = Circle(5.0)
r = Rectangle(10.0, 20.0)Pattern Matching ​
Use match to handle each case:
fn area(shape: Shape) -> Float
match shape
Circle(r) -> 3.14159 * r * r
Rectangle(w, h) -> w * h
Triangle(b, h) -> 0.5 * b * h
end match
end fnUnit Variants ​
Variants can have no data:
type Direction = variant
North
South
East
West
end variant
fn opposite(d: Direction) -> Direction
match d
North -> South
South -> North
East -> West
West -> East
end match
end fnOption and Result ​
Option Type ​
Option[T] represents a value that might be absent:
type Option[T] = variant
Some(value: T)
None
end variantUse it instead of null:
fn find_value(lst: List[Int], target: Int) -> Option[Int]
for item in lst with result = None
match item == target
True -> Some(item)
False -> result
end match
end for
end fn
fn main() -> Unit using file_io
numbers = [10, 20, 30, 40]
match find_value(numbers, 30)
Some(n) -> print("Found: ${to_string(n)}")
None -> print("Not found")
end match
end fnResult Type ​
Result[T, E] represents success or failure:
type Result[T, E] = variant
Ok(value: T)
Err(error: E)
end variantUse it for operations that can fail:
fn parse_positive(s: String) -> Result[Int, String]
match to_int(s)
Some(n) ->
match n > 0
True -> Ok(n)
False -> Err("Must be positive")
end match
None -> Err("Invalid number: ${s}")
end match
end fnTuples ​
Tuples group values of different types:
pair = (1, "hello")
triple = (True, 3.14, "test")
-- Destructuring
(flag, value, label) = tripleLists ​
Lists hold multiple values of the same type:
numbers = [1, 2, 3, 4, 5]
names = ["Alice", "Bob", "Carol"]
empty: List[Int] = []Accessing Elements ​
Use get_at to access elements by index (returns Option since index might be out of bounds):
numbers = [10, 20, 30, 40]
-- get_at(index, list) - pipe-friendly
first = numbers | get_at(0) -- Some(10)
third = numbers | get_at(2) -- Some(30)
oob = numbers | get_at(99) -- None (out of bounds)
-- Handle the Option
match numbers | get_at(0)
Some(n) -> print("First: ${to_string(n)}")
None -> print("List is empty")
end matchFor convenience, use head and last:
first = head(numbers) -- Some(10)
final = last(numbers) -- Some(40)Common Operations ​
-- Length
len = length(numbers)
-- Append (pipe-friendly)
more = numbers | append(6)
-- Concatenate
all = numbers | concat([6, 7, 8])
-- Transform
doubled = numbers | map(fn(x: Int) -> x * 2)
-- Filter
evens = numbers | filter(fn(x: Int) -> x % 2 == 0)
-- Reduce
total = numbers | fold(0, fn(acc: Int, x: Int) -> acc + x)Function Type Aliases ​
Use type with function type syntax to define named function type aliases. This is how you describe the type of functions that can be passed around as values.
Basic Signatures ​
-- A function that takes an Int and returns a Bool
type Predicate = (Int) -> Bool
-- A function that transforms an Int
type IntTransform = (Int) -> Int
-- A function that combines two values
type Combiner = (Int, Int) -> IntUsing Signatures ​
Signatures are used as types for function parameters and return values:
type Predicate = (Int) -> Bool
type IntTransform = (Int) -> Int
-- Function that takes a predicate as parameter
fn count_matching(p: Predicate, lst: List[Int]) -> Int
filter(p, lst) | length
end fn
-- Function that returns a function
fn make_multiplier(n: Int) -> IntTransform
fn(x: Int) -> x * n
end fn
fn main() -> Unit using file_io
is_even = fn(x: Int) -> x % 2 == 0
nums = [1, 2, 3, 4, 5, 6]
print(to_string(count_matching(is_even, nums))) -- 3
double = make_multiplier(2)
print(to_string(double(5))) -- 10
end fnGeneric Signatures ​
Signatures can have type parameters:
-- Generic predicate
type Predicate[T] = (T) -> Bool
-- Generic transformer
type Transform[T] = (T) -> T
-- Mapper between types
type Mapper[A, B] = (A) -> B
-- Reducer/fold function
type Reducer[Acc, T] = (Acc, T) -> AccSignatures with Effects ​
Signatures can declare effects:
-- A function with IO effect
type Printer = (Int) -> Unit with IO
-- A function that can fail
type Validator = (Int) -> Int with Fail[String]
-- A quantum gate
type Gate = (Qubit) -> Qubit with QuantumEffect-Polymorphic Signatures ​
Use effect E for signatures that work with any effect:
-- Works with pure functions OR effectful functions
type Transform[T, effect E] = (T) -> T with E
-- Use it in higher-order functions
fn twice[T, effect E](f: Transform[T, E], x: T) -> T with E
f(f(x))
end fn
-- Works with pure functions
twice(fn(x: Int) -> x + 1, 5) -- 7
-- Works with IO functions
fn greet(s: String) -> String with IO
print("Hello!")
s
end fn
fn main() -> Unit using file_io
twice(greet, "World") -- prints twice
end fnSignatures with Capabilities ​
Signatures can require capabilities:
type ReversibleGate = (Qubit) -> Qubit with Quantum is ReversibleStandard Library Signatures ​
Kettle provides common signatures in stdlib/sigs.ket:
| Signature | Shape | Use Case |
|---|---|---|
Predicate[T] | (T) -> Bool | Filtering, conditionals |
Transform[T] | (T) -> T | Same-type transformations |
Mapper[A, B] | (A) -> B | Type-changing maps |
Reducer[Acc, T] | (Acc, T) -> Acc | Folds, accumulations |
Consumer[T] | (T) -> Unit | Side effects |
Producer[T] | () -> T | Value generation |
Gate | (Qubit) -> Qubit with Quantum | Quantum gates |
Type Annotations ​
While Kettle infers most types, you can add annotations:
-- Variable annotation
count: Int = 0
-- Function with full annotations
fn add(a: Int, b: Int) -> Int
a + b
end fn
-- Empty collections need annotations
empty_list: List[String] = []Next Steps ​
- Generics — Parameterized types
- Functional Programming — Working with data using pipes and HOFs