Menu

Funções em Zero: fun, pub fun, parâmetros e tipos de retorno

Como funções funcionam em Zero: a palavra-chave fun, parâmetros tipados, tipos de retorno, o modificador de visibilidade pub e o papel de raises na assinatura.

Esta página tem editores executáveis — edite, execute e veja a saída na hora.

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 com valor.

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 shape pequeno para isso — fica mais claro do que retornar uma tupla-de-pares-de-tuplas.
  • Funções Void só fazem chamadas com check; 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 recebe world e, 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 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 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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR