Pular para o conteúdo

Especificação §13

Dispatch por tipo (ex-overloading)

language-design.md §13 · 45 linhas · 2 min de leitura

Não há keyword overload. “Mesmo nome, implementação diferente por tipo” é só dispatch por tipo em comptime, expresso com construtos que já existem (match sobre type, generics whitelist), exatamente como match absorveu switch e select. São duas formas, conforme as variações são pequenas e juntas ou grandes e separadas.

Quando as variações são pequenas, um whitelist generic com match T no corpo. O [...] restringe T a um conjunto fechado; o match T escolhe o galho em compilação; o compilador monomorfiza e inlina:

fn to_string[T: {bool, int, float}](x: T) -> string {
match T {
bool => return x ? "true" : "false"
int => return int_to_str(x)
float => return float_to_str(x)
}
}
to_string(42) // T=int → galho int (resolvido em compilação)
to_string(true) // T=bool

Como type é valor de primeira classe (seção 10), match T é o mesmo match de sempre: casa um valor-tipo contra literais-tipo, igual a match código { 200 => ... }. E o whitelist é domínio fechado, então o match T é exaustivo e proíbe _ (erro de compilação): adicionar um tipo ao whitelist quebra a compilação até você tratá-lo.

Quando as implementações são grandes ou moram em arquivos diferentes, elas vivem como funções separadas e um set as reúne sob um nome:

fn bool_to_string(b: bool) -> string { ... }
fn int_to_string(i: int) -> string { ... }
fn to_string = { bool_to_string, int_to_string } // o set

fn nome = { ... } reúne funções existentes num roteador despachado por tipo, resolvido em compilação (sem custo de runtime). É o equivalente ao proc{...} do Odin, com keywords que já temos.

Regra de exclusividade mútua: as assinaturas no set têm que ser distinguíveis por tipo, e duas funções que possam casar o mesmo argumento são erro na declaração do set, não desempate por precedência. O set é um roteador 1-para-1 por tipo, nunca uma busca com coerção implícita. É o que separa isto do overload-hell de C++.

Variações pequenas e juntas pedem match T interno (menos boilerplate). Variações grandes ou separadas pedem o set externo. A antiga distinção “generic vs overload” vira uma escolha de corpo: lógica uniforme usa um generic normal (fn f[T + Display](x: T)), e lógica que difere por tipo usa um dos dois acima.

Os dois despacham por tipo, com aridade fixa. “Mesmo nome, número de argumentos diferente” não é overload aqui: é parâmetro default (fn f(x: int = 5), em que chamar f() usa o 5) ou variádico (fn f(xs: ...int)), que cobrem o caso sem reintroduzir resolução de overload.