Skip to content

Data Types ​

Kettle has a rich set of data types for structuring your programs.

Records ​

Records are named collections of fields:

kettle
type Point = record
  x: Float
  y: Float
end record

type Person = record
  name: String
  age: Int
end record

Creating Records ​

kettle
origin = Point { x: 0.0, y: 0.0 }
alice = Person { name: "Alice", age: 30 }

Accessing Fields ​

Use dot notation:

kettle
print(alice.name)           -- "Alice"
print(to_string(origin.x))  -- "0.0"

Updating Records ​

Create a new record with some fields changed:

kettle
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:

kettle
type Shape = variant
  Circle(radius: Float)
  Rectangle(width: Float, height: Float)
  Triangle(base: Float, height: Float)
end variant

Creating Variants ​

kettle
c = Circle(5.0)
r = Rectangle(10.0, 20.0)

Pattern Matching ​

Use match to handle each case:

kettle
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 fn

Unit Variants ​

Variants can have no data:

kettle
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 fn

Option and Result ​

Option Type ​

Option[T] represents a value that might be absent:

kettle
type Option[T] = variant
  Some(value: T)
  None
end variant

Use it instead of null:

kettle
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 fn

Result Type ​

Result[T, E] represents success or failure:

kettle
type Result[T, E] = variant
  Ok(value: T)
  Err(error: E)
end variant

Use it for operations that can fail:

kettle
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 fn

Tuples ​

Tuples group values of different types:

kettle
pair = (1, "hello")
triple = (True, 3.14, "test")

-- Destructuring
(flag, value, label) = triple

Lists ​

Lists hold multiple values of the same type:

kettle
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):

kettle
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 match

For convenience, use head and last:

kettle
first = head(numbers)  -- Some(10)
final = last(numbers)  -- Some(40)

Common Operations ​

kettle
-- 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 ​

kettle
-- 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) -> Int

Using Signatures ​

Signatures are used as types for function parameters and return values:

kettle
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 fn

Generic Signatures ​

Signatures can have type parameters:

kettle
-- 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) -> Acc

Signatures with Effects ​

Signatures can declare effects:

kettle
-- 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 Quantum

Effect-Polymorphic Signatures ​

Use effect E for signatures that work with any effect:

kettle
-- 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 fn

Signatures with Capabilities ​

Signatures can require capabilities:

kettle
type ReversibleGate = (Qubit) -> Qubit with Quantum is Reversible

Standard Library Signatures ​

Kettle provides common signatures in stdlib/sigs.ket:

SignatureShapeUse Case
Predicate[T](T) -> BoolFiltering, conditionals
Transform[T](T) -> TSame-type transformations
Mapper[A, B](A) -> BType-changing maps
Reducer[Acc, T](Acc, T) -> AccFolds, accumulations
Consumer[T](T) -> UnitSide effects
Producer[T]() -> TValue generation
Gate(Qubit) -> Qubit with QuantumQuantum gates

Type Annotations ​

While Kettle infers most types, you can add annotations:

kettle
-- 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 ​