Slide 34 de 74

Genéricos

Um chamável genérico
func first(v: T[]): Result<T, Bool> {
	if len(v) > 0 {
		return Result.Ok(v[0])
	}
	return Result.Error(false)
}

proc main(): void {
	a := first([1, 2, 3])       // first_Int
	b := first(["x", "y"])      // first_Str, uma cópia separada
	echo "{a} {b}"
}
Um tipo genérico
type Box {
	items: T[dyn]
	label: Str
}

type Either {
	Left  { left: A }
	Right { right: B }
}

proc main(): void {
	Box<Str> people = Box(["ada", "grace"], "people")
	Box<Int> counts = Box([1, 2, 3], "counts")
	Either<Str, Int> answer = Either.Right(42)

	echo "{people.label}: {people.items}"
	echo "{counts.label}: {counts.items}"
	if answer is Either.Right(n) { echo n }
}

Um nome em uma assinatura que não é nem um tipo nativo nem um declarado é uma variável de tipo, e ele torna o chamável genérico. Uma variável é fixada por onde aparece entre os parâmetros — inclusive dentro do próprio tipo de função de um parâmetro. Nada nela é dinâmico: toda chamada é resolvida quando o programa compila, e uma cópia concreta é escrita para cada conjunto distinto de argumentos de tipo — first_Str, first_Int — sem boxing e sem despacho. Um tipo pode receber variáveis do mesmo jeito, e é escrito com elas preenchidas: Box<Str>, Either<Str, Int>.

Vale saber:

  • Um nome de tipo escrito errado vira uma variável de tipo em vez de um erro: func greet(u: Usr): Str é genérico, e aceita qualquer coisa. Confira a grafia quando uma função aceitar mais do que deveria.
  • Uma variável precisa aparecer em um parâmetro, porque é ali que uma chamada a fixa; uma que aparece só no tipo de retorno é recusada.
  • O corpo de um genérico é checado uma vez por cópia, quando é chamado, então um erro em um que ninguém chama passa sem aviso, e um erro em uma cópia chamada é reportado com o nome dela, como broken_Int.
  • A variante de uma união genérica é construída onde uma declaração diz qual tipo ela é — Either<Str, Int> answer = Either.Right(42) — já que a variante sozinha não diz o que o outro lado guarda.
  • Um chamável genérico não é um valor: qual cópia uma referência quer dizer depende de tipos de argumento que ela não tem.