# Array literals

> An array literal [a, b, c] is a value: a list you can return, assign, pass as an argument, or supply as a UI prop. Its element type is inferred from the items. Object literals inside it (new T { … }) are plain data, so a render prop like columns: [ new GridColumn<User> { Name = "Email", Value = u => u.Email } ] is fully expressible.

<!-- id: function-collection-literals · area: function · stability: preview · html: https://osysharp.com/reference/function/collection-literals/ -->

## Summary        {#summary}
An **array literal** `[a, b, c]` is a value expression — a list you can return, assign to a local, pass as an
argument, or hand to a UI control as a prop. The element type is inferred from the items (a homogeneous list is the
common case). Because object literals (`new T { … }`) are plain data, a list of them is a first-class value too — so
a UI prop like `columns: [ new GridColumn<User> { Name = "Email", Value = u => u.Email } ]` is expressed directly,
no workaround.

## Signature      {#signature}
```osy syntax
[1, 2, 3]                                     // int[]
["Email", "DisplayName"]                       // string[]
[ new GridColumn<User> { Name = "Email", Value = u => u.Email } ]   // GridColumn<User>[]
[]                                             // an empty list
```

## Description    {#description}
- **A value, anywhere a value fits** — return it, assign it (`var xs = [1, 2, 3];`), pass it as an argument, or use
  it as a render/state value in a component.
- **Element type is inferred** from the items; a list of `new GridColumn<User> { … }` is a `GridColumn<User>[]`,
  matching a prop declared `GridColumn<T>[]`.
- **Object literals inside are pure data.** In a render position a `new T { … }` builds a plain data object (it is
  *not* a stored row — that is what a `new T { … }` inside an action body does). This is why a control's `columns`
  prop takes `[ new GridColumn<T> { … } ]` cleanly.
- **Distinct from set-membership.** Testing membership is written `[a, b].Contains(x)` (or `x in [a, b]`), which the
  compiler lowers to a set test — separate from using `[a, b]` as a value.

### It takes the type it is written into   {#target-typed}
As in C# 12, a literal is **target-typed**: written where a `List<T>`, a `HashSet<T>` or a `byte[]` is expected, it
*is* one — `List<string> tags = [];` needs no `.ToList()`. An empty `[]` written where a `Dictionary<K, V>` is
expected is an empty dictionary, and where a `HashSet<T>` is expected an empty set (a value added to it twice is held
once). The declaration decides, wherever it is written — a local, a class's field, a component's field — and the
value is the same collection on the server and in the browser. That holds for a return, an argument, a field initializer,
each branch of a `?:`, and **the right-hand side of `??`**, which takes the LEFT operand's collection type (or, failing
that, the type the whole expression is written into). So the null fallback C# code writes everywhere is just that:

```osy title="an empty (or default) list when there is none" test app=collection-literals-coalesce
class Draft { public List<string>? Tags; }

List<string> TagsOf(Draft d) => d.Tags ?? [];                // the empty list, as a List<string>
List<string> TagsOrDefault(Draft d) { return d.Tags ?? ["untagged"]; }

int WithOneMore(Draft d) {
  var tags = d.Tags ?? [];        // a List<string> (the left side's type), so it can grow
  tags.Add("new");
  return tags.Count;
}
```

```osy title="the fallback is a real list" run app=collection-literals-coalesce
[Test]
void The_fallback_takes_the_left_sides_type() {
  Assert.Equal(0, TagsOf(new Draft()).Count);
  Assert.Equal(["untagged"], TagsOrDefault(new Draft()));
  Assert.Equal(["a"], TagsOrDefault(new Draft { Tags = ["a"] }));
  Assert.Equal(1, WithOneMore(new Draft()));
}
```

⚠ **Only a literal is retyped.** An existing `T[]` on the right of `??` is not turned into a `List<T>`: an array is a
read-only sequence (usually a query result), and handing it back as a list would promise an `.Add` it cannot keep.
Write `.ToList()` when that is what you mean.

### Where can `new()` leave out the type?   {#target-typed-new}
C# 9's target-typed `new()` takes the type of the place it is written — a declared local, a class's or a component's
field (`public Dictionary<string, string> Values = new();`), a return, an argument, an initializer member: `List<int> xs = new();`, `Point p = new();`. With braces it is that type's initializer:
`new() { 1, 2 }` for a list or set, `new() { ["k"] = 1 }` for a dictionary, `new() { X = 1 }` for a class. Where
the position gives no type — a lambda's body, an interpolation hole, the right of `??` — C# casts, and so does Osy#:
`(List<int>)[1, 2]`, `(int[])[]`, `(Point)new()`.

```osy title="the type comes from where it is written" test app=collection-target-new
int Counts() {
  List<int> xs = new(); xs.Add(1);
  HashSet<int> ids = new() { 1, 2, 2 };                // a set: 2 elements
  Dictionary<string, int> stock = new() { ["a"] = 3 };
  return xs.Count + ids.Count + stock["a"] + ((List<int>)[7, 8]).Count;   // 1 + 2 + 3 + 2
}
```

### Can a page send one to the server?   {#crossing}
Yes. A `List<T>`, a `HashSet<T>` and a `Dictionary<K, V>` are passed to a server function from a component's action
as any value is — as the argument itself, or as a member of a class that is — and come back the same way. A
dictionary or set made with `StringComparer.OrdinalIgnoreCase` still compares that way on the other side.

```osy title="a class with a dictionary, sent from a page" syntax
public class Reply {
  public string Comment = "";
  public Dictionary<string, string> Values = [];
}

public string Send(Reply reply) { return reply.Comment + ": " + reply.Values.Count; }

component Form() {
  Dictionary<string, string> typed = [];
  string said = "";
  action Submit() { said = Send(new Reply { Comment = "done", Values = typed }); }
  render { Pressable(onClick: Submit) { Text("Send " + said); } }
}
```

### Is there a `Queue<T>` or a `Stack<T>`?   {#queue-stack}
Not yet. The collections are `List<T>`, `HashSet<T>`, `Dictionary<K, V>` and arrays (`T[]`); `new Queue<int>()`,
`new Stack<int>()` and `new LinkedList<int>()` are refused by name, each with its stand-in. A list does all three
jobs:

```osy title="a queue and a stack held in a list" test app=collection-queue-stack
/// First in, first out: 1 then 2.
int ServeNext() {
  var queue = new List<int>();
  queue.Add(1); queue.Add(2);                       // enqueue
  var next = queue[0]; queue.RemoveAt(0);           // dequeue
  return next * 10 + queue[0];                      // 12 — and queue[0] peeks
}

/// Last in, first out: 4 then 3.
int UndoLast() {
  var stack = new List<int>();
  stack.Add(3); stack.Add(4);                                        // push
  var top = stack[stack.Count - 1]; stack.RemoveAt(stack.Count - 1); // pop
  return top * 10 + stack[stack.Count - 1];                          // 43
}
```

## Examples       {#examples}
```osy title="an array literal as a value" test app=collection-literals
int[] Small() { return [1, 2, 3]; }
string[] Names() { var xs = ["Ada", "Grace"]; return xs; }
```

Supplying a control's typed columns from an array of object literals (a UI prop):

```osy title="object literals as a control's typed columns prop" test app=collection-literals-typed-columns
entity User {
  [Required, MaxLength(200)] string Email;
  [Required, MaxLength(120)] string DisplayName;
  security { allow read when IsAuthenticated; }
}

[Route("/users")] [Render(CSR)]
component UsersPage() {
  var users = User.ToList();
  render {
    DataGrid(
      label: "Users",
      rows: users,
      columns: [
        new GridColumn<User> { Name = "Email", Label = "Email", Value = u => u.Email },
        new GridColumn<User> { Name = "DisplayName", Label = "Name", Value = u => u.DisplayName }
      ]
    );
  }
}
```

## See also       {#see-also}
- [List indexer](https://osysharp.com/reference/function/list-indexer/) — reading an element by index (`xs[0]`)
- [List OrderBy (in-memory)](https://osysharp.com/reference/function/list-orderby/) — sorting a collection
- [control — foreign UI controls (charts, grids, maps)](https://osysharp.com/reference/ui/controls/) — foreign controls, whose typed props often take an array literal
