type User {
id: Int
name: Str
}
proc main(): void {
u := User(1, "ada")
echo u.name
echo User(name: "grace", id: 2).id // por nome, em qualquer ordem
}type Event {
at: Int // fora de todas as variantes: toda uma o tem
Opened { by: Str }
Closed
}
proc main(): void {
e := Event.Opened("ada", 5) // o campo próprio da variante primeiro, depois `at`
echo e.at // 5 — um campo compartilhado é lido direto
// echo e.by // erro de compilação: `by` pertence a Opened
for each ev in [e, Event.Closed(9)] {
echo ev.at
}
}type Expr {
Num { value: Int }
Add { left: Expr, right: Expr }
}
type Folder {
name: Str
children: Folder[dyn] // através de um vetor
}
proc main(): void {
sum := Expr.Add(Expr.Num(1), Expr.Num(2))
echo sum
echo Folder("root", [Folder("docs", [])]).name
}Um type sem variantes é um struct; com variantes, uma união com tag. Um valor é construído chamando o tipo ou a variante — User(1, "ada"), Shape.Circle(5) — e uma variante que não carrega nada também é chamada: Shape.Point(), já que Shape.Point sozinho nomeia a variante e não um valor dela. Os campos podem ser passados por nome, em qualquer ordem. Não existe subtipagem nem interface: uma variante é uma forma de construir e casar uma união, não um tipo próprio.
Um campo declarado fora de todas as variantes é adicionado a todas elas, depois dos campos próprios de cada variante, e é o único tipo de campo que um valor de união tem: event.at o lê direto. Todo outro campo pertence à sua variante e é alcançado casando com is, nos próximos slides — mesmo um que toda variante declare com o mesmo nome. Um valor de união é construído inteiro, então nenhum campo dele é atribuído sozinho.
Um tipo pode conter a si mesmo através de uma variante, que é o que uma árvore precisa, ou de um vetor. Um struct que contém a si mesmo diretamente, ou através de um Result, nunca poderia ser terminado, e é recusado.