A anatomia de uma função
A forma geral de uma função Zero:
fun nome(param1: Tipo1, param2: Tipo2) -> TipoRetorno {
// corpo
return valor
}
Peças:
fun— a palavra-chave que introduz uma função.nome— o nome da função.(param1: Tipo1, ...)— lista de parâmetros. Todo parâmetro tem tipo explícito.-> TipoRetorno— o tipo de retorno.{ ... }— o corpo, um bloco de instruções.return valor— sai da função comvalor.
Um exemplo concreto pequeno:
fun double(value: i32) -> i32 {
return value * 2
}
Essa é a função inteira. Recebe um i32, retorna outro e não faz I/O. De dentro do corpo, value é uma ligação no estilo let — você pode usar como qualquer outra variável local.
Chamando funções
Chamadas se parecem exatamente com o que você esperaria:
let result = double(21)
O argumento precisa bater com o tipo do parâmetro. O resultado é vinculado a result, que o compilador infere ser i32 porque double retorna i32.
Um exemplo amarrando um auxiliar e um main — clique em Run para ver em ação:
Você deve receber math works\n no stdout.
pub e visibilidade
Por padrão, uma função declarada num arquivo é privada a esse arquivo (ou módulo — as regras de visibilidade ficam mais estritas conforme os projetos crescem). Para expor uma função fora do módulo, coloque pub na frente:
pub fun greet() -> String {
return "hello\n"
}
Sem pub, código em outros módulos não consegue chamar greet. O runtime precisa chamar main de fora de qualquer módulo definido pelo usuário, e é por isso que main é sempre pub.
A regra do "privado por padrão" é boa de seguir. Marque só o que você quer que seja interface; o resto fica dentro do módulo.
Tipos de retorno
Toda função declara o tipo de retorno depois de ->. Tipos de retorno comuns:
fun answer() -> i32 { return 42 }
fun ok() -> bool { return true }
fun label() -> String { return "ready\n" }
fun nothing() -> Void { }
Void é o tipo de retorno para uma função que faz o trabalho dela através de efeitos colaterais em vez de produzir um valor. Uma função Void não precisa de um return explícito — cair pelo fim do corpo basta.
fun log(world: World, message: String) -> Void raises {
check world.out.write(message)
}
Chamadas de função que descartam um valor
Se uma função retorna um valor e você não se importa com ele, ainda precisa reconhecer o retorno. O idiomático é vincular com let:
ignored é uma ligação que o resto da função nunca lê. A convenção de usar o nome ignored (ou _) sinaliza que o descarte é intencional. É mais atrito do que descartar silenciosamente um valor de retorno, e esse é o ponto: numa linguagem em que agentes estão lendo e gerando código, um valor não lido costuma ser um bug que vale expor.
O papel de raises
Uma função que pode falhar declara na assinatura. Vimos isso em main:
pub fun main(world: World) -> Void raises {
check world.out.write("hello\n")
}
A cláusula raises pode ser simples (qualquer erro) ou específica:
fun validate(ok: Bool) -> i32 raises { InvalidInput } {
if ok == false {
raise InvalidInput
}
return 42
}
raises { InvalidInput } significa "esta função pode falhar com InvalidInput, e nada mais". Quem chama precisa usar check (ou uma forma mais elaborada de tratamento) para propagar ou tratar o erro.
Raises e Check aprofunda isso, incluindo o que acontece com múltiplos tipos de erro e como check interage com a cláusula raises do chamador.
Funções genéricas
Quando você quer que uma função funcione com mais de um tipo, declare parâmetros de tipo entre sinais de menor/maior:
fun makePair<T, U>(left: T, right: U) -> Pair<T, U> {
return Pair { left: left, right: right }
}
T e U são parâmetros de tipo — quem chama decide o que são. Chamar makePair(40, 2_u8) te dá um Pair<i32, u8>. Veja Generics para a história completa, incluindo shapes genéricos e restrições.
Onde as funções moram
Num programa pequeno, você escreve funções diretamente no arquivo .0. Num pacote, você espalha funções por arquivos dentro de src/ e o compilador resolve referências entre arquivos para você. Os fundamentos continuam os mesmos — fun, parâmetros, tipo de retorno, corpo — independente de onde a função fica fisicamente.
Notas de estilo
Algumas convenções que você vai ver nos exemplos oficiais:
- Nomes de função em minúsculas, palavras juntas (
makePair) ou separadas por camelCase. A biblioteca padrão tende ao camelCase. - Um valor de retorno por função. Se você precisa retornar várias coisas, monte um
shapepequeno para isso — fica mais claro do que retornar uma tupla-de-pares-de-tuplas. - Funções
Voidsó fazem chamadas comcheck; funções que computam um valor evitam I/O quando podem. Essa separação é em parte cultural e em parte imposta — uma função de computação pura não recebeworlde, portanto, literalmente não consegue fazer I/O.
O último ponto vale insistir. Como I/O vive atrás da capacidade World e World é passado explicitamente, a assinatura de uma função te diz se ela pode fazer I/O. Funções cujas assinaturas não mencionam World são puras com relação ao mundo externo. É uma propriedade em que agentes (e humanos) podem confiar sem ler o corpo.
A seguir: if/else
Você já viu if aparecer de passagem — a próxima documentação cobre expressões if/else em detalhe, incluindo como elas interagem com ligações e o que está faltando de propósito (sem coerção de truthiness, sem ternário).
Perguntas frequentes
Como você declara uma função em Zero?
Use fun: fun nome(param: Tipo) -> TipoRetorno { corpo }. Coloque pub na frente para tornar a função visível fora do módulo. Adicione raises depois do tipo de retorno se a função pode falhar. Por exemplo: pub fun double(value: i32) -> i32 { return value * 2 }.
O que a palavra-chave pub faz?
pub faz?pub marca uma declaração como pública — visível para código fora do módulo atual. Sem pub, uma função é privada ao arquivo (ou pacote) em que foi declarada. O ponto de entrada convencional pub fun main precisa ser público para o runtime conseguir achar e chamar.
Como retornar um valor de uma função em Zero?
Escreva return valor dentro do corpo da função. A expressão precisa ter o tipo declarado de retorno. Uma função com tipo de retorno Void não retorna nada e não precisa de uma instrução return explícita — cair pelo final do corpo está ok.
Funções Zero podem ter múltiplos parâmetros?
Sim. Liste entre parênteses separados por vírgula, cada um com nome e tipo: fun add(a: i32, b: i32) -> i32 { return a + b }. Cada parâmetro é uma ligação no estilo let no corpo da função. Zero exige tipos explícitos nos parâmetros — não há inferência de tipo de parâmetro na declaração da função.
O que raises significa numa assinatura de função?
raises significa numa assinatura de função?raises declara que a função pode falhar. Um raises simples permite qualquer tipo de erro; raises { InvalidInput } restringe a um erro nomeado específico. Quem chama precisa usar check (ou outra construção de falibilidade) para reconhecer a possibilidade de falha — não dá para ignorar em silêncio.