Nested Types

This example defines an Order and gives it a nested Status type for tracking whether it has shipped. Keeping the two types in the same module lets callers import Order once and refer to its status as Order.Status.

Here is the type module in Order.roc:

Order := { item : Str, status : Status }.{
	Status := [Pending, Shipped].{
		is_eq : _
	}

	new : Str -> Order
	new = |item| { item, status: Pending }

	ship : Order -> Order
	ship = |order| { ..order, status: Shipped }
}

Order is a record that holds an item and its status. Inside its associated block, Status defines the states that an order can have, alongside methods for creating and shipping an order.

The app can use both types through the same import:


import Order

order = Order.new("Roc mug")

shipped = order.ship()

status : Order.Status
status = shipped.status

expect order.status == Order.Status.Pending
expect status == Order.Status.Shipped
expect shipped.item == "Roc mug"

This grouping gives the status a clear owner: it is an order status, rather than a general-purpose Status type. Since a type module exposes one top-level type, nesting also lets that type’s public API include related types without putting each in a separate module.

Nesting is independent of recursion. The Recursive Types example shows types that refer to themselves and each other without an enclosing type.

Output

Run this from the directory that has main.roc in it:

$ roc test main.roc

All (3) tests passed in 9.5 ms.

Validated with Roc nightly-2026-10-29-a0a9fc2.