Collections
List<T>, Set<T> and Map<K, V> can be used as field types, parameter types and boundary types, nested in each other or holding objects.
List<T>, Set<T> and Map<K, V> can be used as field types, parameter
types and boundary types, nested in each other or holding objects.
type BasketLine = Object { tag: Tag stock: Stock}type Basket = Output { lines: List<BasketLine> tags: List<Tag> { max_length: 10 }}
A collection keeps its element type: a List<Customer.email> is not a
List<Email>. Nothing converts one collection into another:
List<Tag>(names) is type.constructor_input, since only a named type is
built from a value. A named collection type is a named type, so it can be
built from a collection of its items (Named collection construction).
How you build and walk them:
- A list literal is written in square brackets, its items taking the
list's element type:
["urgent", "home"]. Written where a set is expected, it is a set. forwalks a list (Logic). It walks a set too, in the set order below, and a map through its keys:for key in map.keys(prices).- Operations.
list.count,list.contains,list.mapandlist.filtertake a list. Theset.andmap.operations search and rebuild sets and maps (Sets, Maps). - An empty map is
[]written where a map is expected, andmap.withadds entries to it.
In JSON, a Set<T> is an array and a Map<K, V> an object:
{ "tags": ["urgent", "home"], "weights": { "urgent": 3, "home": 1 } }- An array that repeats a value is refused where a set is expected.
- A map in an
InputorOutput, however deeply nested, is keyed by a type built onTextorUuid, because JSON object keys are text (type.map_key_boundary). A standard named collection such asLocalisedTextis sent as its own entry in the standard library says.
Sets have rules of their own:
- A set literal that repeats a value is
type.invalid_literal. - A list written where a named set type is expected
(
type Tags = Set<TagName> {}) is a set, so[b, a]equals[a, b]. - Equal computed items collapse into one, so the compiler can't count them
ahead of time. A set literal's
min_lengthcounts only its distinct literals, or one value when it has any items:[a, b]into a set type withmin_length: 2istype.invalid_literal.
Set order. A set has no order your code can use: no index and no first
item. Where order matters, sort it or use a List. Everything that shows a
set (JSON, reports, for) uses one fixed order, the same in interpreted and
compiled code: by value, numbers numerically, text by Unicode code point,
enum variants in declaration order, and objects and tagged unions field by
field in declared order.
Named collection construction
A named list, set or map type is built from a collection whose items are
already its item type. It is an ordinary construction, Type(value), that
checks only the collection's own rules: the min_length and max_length of
the type and of the types it builds on. When one of them can break, the
construction takes attempt and an arm for each, or invalid: for all of
them. The declared lengths and the conditions around it prove it as they
prove any construction, and a proven one is plain.
type Guests = List<GuestName> { min_length: 1 max_length: 12}type GuestBook = Set<GuestName> { min_length: 1}function guest_list(invited: List<GuestName>) fails NoGuests, TooManyGuests -> Guests { var guests: Guests = attempt Guests(invited) { // any list, so both can break min_length: NoGuests max_length: TooManyGuests } return guests}function seated(invited: List<GuestName>) fails NoGuests, TooManyGuests -> Guests { if (list.count(invited) == 0) { reject NoGuests } else if (list.count(invited) > 12) { reject TooManyGuests } return Guests(invited) // 1 to 12 names here}function signed_in(guests: Guests) -> GuestBook { return GuestBook(guests) // at least one name, each kept once}
- The items already are the item type. Building the collection never
converts its items: a
Textbecomes aGuestNamewhere it is built, each with its own arms. A collection type never stands in for another, so a list of a type built onGuestNamedoesn't fit either. - A list from a list, a map from a map. A map's keys and values must both already be its own. A list is never built from a set, since a set has no order a list could keep.
- A set from a set or a list. A list's repeats collapse, and the set
keeps the set order (Collections). It holds at most as many items
as the list, so the list's length proves the set's
max_length, and at least one when the list has any, so the list proves amin_lengthof 1 and no more: two of its items might be equal. - From another named collection. A narrower named collection widens to the one it builds on with no construction at all. One of the same item type that doesn't build on it is built like any value, its own declared lengths proving what they can.
- Field types too. A field type with collection rules is built the
same way:
Party.guests(invited). - A list written in the construction takes the collection's type and
its length is checked while compiling, as a literal's is:
Guests([])istype.invalid_literal. - Only a named type.
List<GuestName>(invited)staystype.constructor_input: a collection type that isn't named is never built from a value.
Passing a collection whose items aren't the item type yet is
type.constructor_input, and its next says where to build them:
type GuestName = Text { min_length: 1}type Guests = List<GuestName> { min_length: 1}type RawName = Text {}type NoGuests = Failure { kind: FailureKind.invalid_value}function guest_list(invited: List<RawName>) fails NoGuests -> Guests { var guests: Guests = attempt Guests(invited) { min_length: NoGuests } return guests}
error: `Guests` can't be built from `List<RawName>` --> main.jadpo:16:41 |16 | var guests: Guests = attempt Guests(invited) { | ^^^^^^^ this is `List<RawName>` reason: `Guests` is a list of `GuestName`, and this is a list of `RawName`. Building a `Guests` checks only the list's own rules; it never converts the items. next: Build each item as a `GuestName` where it is made, so the list is a `List<GuestName>` (agent) rule: type.constructor_input help: https://jadpo.dev/docs/errors/type.constructor_inputcheck failed: 1 error, 1 file
A set where a named list is needed is the same error:
type GuestName = Text { min_length: 1}type Guests = List<GuestName> { min_length: 1}type NoGuests = Failure { kind: FailureKind.invalid_value}function guest_list(invited: Set<GuestName>) fails NoGuests -> Guests { var guests: Guests = attempt Guests(invited) { min_length: NoGuests } return guests}
error: `Guests` can't be built from `Set<GuestName>` --> main.jadpo:14:41 |14 | var guests: Guests = attempt Guests(invited) { | ^^^^^^^ this is `Set<GuestName>` reason: `Guests` is a list of `GuestName`, and this is a set. A set has no order a list could keep, so a list is built only from a list. next: Keep the items in a `List<GuestName>` where they are collected (agent) rule: type.constructor_input help: https://jadpo.dev/docs/errors/type.constructor_inputcheck failed: 1 error, 1 file
Markdown for agents: /docs/collections.md.Built from Jadpo main at a707efb.