ADR 0032: Go's typed literals for slices, arrays, and maps
Status: Accepted (extends 0031)
Context
Section titled “Context”A Lyth collection literal, [a, b] or [k: v], takes its type from a
declaration or parameter, or else from its elements. Where neither gives the
type, there was no way to write it: a response body
["status": "ok", "count": 3] passed to an any parameter has no common
value type, []any argument lists and []int64 values needed a declared
variable first, and arrays could not be written as literals at all. Go
spells all of these with composite literals: map[string]any{...},
[]int64{1, 2}, [3]int{1, 2, 3}. Go’s struct literal Point{X: 1} is a
different matter: Lyth constructs structs by calling the type, with named and
default arguments (ADR 0019).
Decision
Section titled “Decision”[]T{a, b},[N]T{a, b},[...]T{a, b}, andmap[K]V{k: v}are typed literals with Go’s meaning; they lower to the same Go literal. An array literal may give fewer elements than its length (the rest are zero), as in Go;[...]Ttakes the length from the elements.- Only these type forms take braces. They cannot be called with a trailing
lambda or be a struct, so
[]int{is never ambiguous; a named type (http.Header) is converted from a Lyth literal instead:http.Header(["Accept": ["text/plain"]]). - Elements are ordinary expressions, typed by the element type: an inner
collection is written
[1, 2]or with its own type, and a struct element is a construction call. Go’s elided inner literals ({1, 2},{X: 1}) are E0008, because{...}is a lambda in Lyth. Indexed elements ({0: a}) are E0008. - As in Go, a typed literal in an
if,while,for, orwhenheader must be parenthesized (for x in ([]int{1, 2}) { ... }); without parentheses it is E0008 with that edit. - Lyth’s inferred literals stay:
[1, 2],["a": 1],[]and[:]where a type is expected, anda: [3]int = [1, 2, 3], which must give every element. A map literal that gives a constant key twice is E0102 in both forms. - Go’s struct literal stays E0008 with the construction call as its edit
(
Point(X = 1),Page[int](Items = [1])).
Alternatives
Section titled “Alternatives”- A conversion form,
map[string]any(["status": "ok"]): keeps one literal syntax, but reads as a conversion of a value that does not exist and is not Go’s spelling. - Keep declarations as the only way to type a literal: no new syntax, but
common backend code (
map[string]anybodies,[]anyarguments) needs a temporary variable. - Go’s elided inner literals: shorter nested data, but
{...}is a lambda in Lyth, so an element{1, 2}would change meaning with the element type. - Struct literals
Point{X: 1}: a second construction syntax next to the call, which supports defaults and positional arguments, and the same braces as a trailing lambda after a name.
Consequences
Section titled “Consequences”Typed collection data reads as in Go, and the generated Go is the same
literal. There are two ways to write a slice literal when the type is
already known (xs: []int = [1, 2] and xs := []int{1, 2}); the guide
recommends the inferred form where a type is already given and the typed
form where it is not. Tests: run/typedlit, gen/typedlit,
check/typedlit_neg, and the parser tests.