La anatomía de una función
La forma general de una función en Zero:
fun name(param1: Type1, param2: Type2) -> ReturnType {
// cuerpo
return value
}
Las piezas:
fun— la palabra clave que introduce una función.name— el nombre de la función.(param1: Type1, ...)— lista de parámetros. Cada parámetro tiene un tipo explícito.-> ReturnType— el tipo de retorno.{ ... }— el cuerpo, un bloque de instrucciones.return value— sale de la función convalue.
Un ejemplo pequeño y concreto:
fun double(value: i32) -> i32 {
return value * 2
}
Esa es toda la función. Toma un i32, devuelve otro y no hace E/S. Desde dentro del cuerpo, value es una ligadura tipo let — puedes usarla como cualquier otra variable local.
Llamar a funciones
Las llamadas tienen el aspecto que esperarías:
let result = double(21)
El argumento tiene que coincidir con el tipo del parámetro. El resultado se vincula a result, cuyo tipo el compilador infiere como i32 porque double devuelve i32.
Un ejemplo que une una función auxiliar y un main — pulsa Ejecutar para verlo en acción:
Deberías ver math works\n por la salida estándar.
pub y visibilidad
Por defecto, una función declarada en un archivo es privada a ese archivo (o módulo — las reglas de visibilidad se ajustan a medida que los proyectos crecen). Para exponer una función fuera de su módulo, antepón pub:
pub fun greet() -> String {
return "hola\n"
}
Sin pub, el código en otros módulos no puede llamar a greet. El runtime necesita llamar a main desde fuera de cualquier módulo definido por el usuario, por eso main siempre es pub.
La regla "privado por defecto" merece la pena aprovecharla. Marca solo lo que quieras que sea interfaz; el resto se queda dentro del módulo.
Tipos de retorno
Cada función declara su tipo de retorno después de ->. Tipos de retorno habituales:
fun answer() -> i32 { return 42 }
fun ok() -> bool { return true }
fun label() -> String { return "ready\n" }
fun nothing() -> Void { }
Void es el tipo de retorno para una función que hace su trabajo a través de efectos secundarios en vez de producir un valor. Una función Void no necesita un return explícito: basta con llegar al final del cuerpo.
fun log(world: World, message: String) -> Void raises {
check world.out.write(message)
}
Llamadas que descartan un valor
Si una función devuelve un valor y no te importa, igualmente tienes que reconocer el retorno. El idioma es vincularlo con let:
ignored es una ligadura que el resto de la función nunca lee. La convención de usar el nombre ignored (o _) señala que el descarte es intencional. Hay más fricción que descartar un retorno silenciosamente, y ese es el punto: en un lenguaje donde los agentes leen y generan código, un valor no leído suele ser un bug que conviene hacer visible.
El papel de raises
Una función que puede fallar lo declara en la firma. Lo vimos en main:
pub fun main(world: World) -> Void raises {
check world.out.write("hola\n")
}
La cláusula raises puede ir pelada (cualquier error) o específica:
fun validate(ok: Bool) -> i32 raises { InvalidInput } {
if ok == false {
raise InvalidInput
}
return 42
}
raises { InvalidInput } significa "esta función puede fallar con InvalidInput, y con nada más". Quienes llaman deben usar check (o una forma de manejo más elaborada) para propagar o manejar el error.
Raises y Check entra en esto en profundidad, incluyendo qué pasa con varios tipos de error y cómo interactúa check con la cláusula raises del llamador.
Funciones genéricas
Cuando quieres que una función trabaje sobre más de un tipo, declara parámetros de tipo entre paréntesis angulares:
fun makePair<T, U>(left: T, right: U) -> Pair<T, U> {
return Pair { left: left, right: right }
}
T y U son parámetros de tipo: quien llama decide qué son. Llamar a makePair(40, 2_u8) te da un Pair<i32, u8>. Mira Genéricos para la historia completa, incluyendo shapes genéricas y restricciones.
Dónde viven las funciones
En un programa pequeño escribes las funciones directamente en tu archivo .0. En un paquete repartes las funciones entre archivos bajo src/ y el compilador resuelve las referencias entre archivos por ti. Lo fundamental no cambia — fun, parámetros, tipo de retorno, cuerpo — sin importar dónde viva físicamente la función.
Notas de estilo
Algunas convenciones que verás en los ejemplos oficiales:
- Nombres de función en minúsculas, palabras juntas (
makePair) o separadas por camelCase. La biblioteca estándar se inclina por camelCase. - Un valor de retorno por función. Si necesitas devolver varias cosas, construye un
shapepequeño: queda más claro que devolver una tupla de pares de tuplas. - Las funciones
Voidsolo hacen llamadascheck; las funciones que computan un valor evitan la E/S cuando pueden. Esa separación es en parte cultural y en parte forzada: una función de cómputo puro no recibeworldy, por tanto, literalmente no puede hacer E/S.
Este último punto merece detenerse. Como la E/S vive detrás de la capacidad World y World se pasa explícitamente, la firma de una función te dice si puede hacer E/S. Las funciones cuyas firmas no mencionan World son puras respecto al mundo exterior. Esa es una propiedad en la que pueden confiar agentes (y humanos) sin leer el cuerpo.
Lo siguiente: If/Else
Has visto aparecer if de pasada — la próxima página cubre las expresiones if/else en detalle, incluyendo cómo interactúan con las ligaduras y qué falta a propósito (sin coerción a verdadero/falso, sin ternario).
Preguntas frecuentes
¿Cómo se declara una función en Zero?
Con fun: fun name(param: Type) -> ReturnType { body }. Añade pub delante para que la función sea visible fuera de su módulo. Añade raises después del tipo de retorno si la función puede fallar. Por ejemplo: pub fun double(value: i32) -> i32 { return value * 2 }.
¿Qué hace la palabra clave pub?
pub?pub marca una declaración como pública: visible para código fuera de su módulo actual. Sin pub, una función es privada al archivo (o paquete) en el que se declara. El punto de entrada convencional pub fun main tiene que ser público para que el runtime pueda encontrarlo y llamarlo.
¿Cómo se devuelve un valor de una función en Zero?
Escribe return value dentro del cuerpo de la función. La expresión tiene que coincidir con el tipo de retorno declarado. Una función con tipo de retorno Void no devuelve nada y no necesita una instrucción return explícita: caer hasta el final del cuerpo basta.
¿Pueden las funciones de Zero recibir varios parámetros?
Sí. Lístalos entre paréntesis separados por comas, cada uno con su nombre y tipo: fun add(a: i32, b: i32) -> i32 { return a + b }. Cada parámetro es una ligadura tipo let en el cuerpo de la función. Zero exige tipos explícitos en los parámetros — no hay inferencia de tipo de parámetro en las declaraciones de función.
¿Qué significa raises en la firma de una función?
raises en la firma de una función?raises declara que la función puede fallar. Un raises pelado permite cualquier tipo de error; raises { InvalidInput } lo restringe a un error nombrado específico. Quienes llaman deben usar check (u otro constructo de falibilidad) para reconocer la posibilidad de fallo: no pueden ignorarlo silenciosamente.