Menu

Zero Functions: fun, pub fun, параметры и возвращаемые типы

Как работают функции в Zero: ключевое слово fun, типизированные параметры, возвращаемые типы, модификатор видимости pub и роль raises в сигнатуре функции.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Анатомия функции

Общая форма функции в Zero:

fun name(param1: Type1, param2: Type2) -> ReturnType {
    // тело
    return value
}

Части:

  • fun — ключевое слово, вводящее функцию.
  • name — имя функции.
  • (param1: Type1, ...) — список параметров. У каждого параметра есть явный тип.
  • -> ReturnType — возвращаемый тип.
  • { ... } — тело, блок инструкций.
  • return value — выходит из функции со значением value.

Маленький конкретный пример:

fun double(value: i32) -> i32 {
    return value * 2
}

Это вся функция. Принимает один i32, возвращает другой, никакого I/O не делает. Внутри тела value — это let-подобная привязка, использовать её можно как любую другую локальную.

Вызов функций

Вызовы выглядят так, как и ожидаешь:

let result = double(21)

Аргумент должен совпадать с типом параметра. Результат привязывается к result, тип которого компилятор выводит как i32, потому что double возвращает i32.

Разобранный пример со вспомогательной функцией и main — нажмите Run, чтобы увидеть его в работе:

Должны увидеть math works\n в stdout.

pub и видимость

По умолчанию функция, объявленная в файле, приватна для этого файла (или модуля — правила видимости становятся строже по мере роста проекта). Чтобы выставить функцию за пределы модуля, поставьте перед ней pub:

pub fun greet() -> String {
    return "hello\n"
}

Без pub код в других модулях вызвать greet не сможет. Рантайму нужно вызвать main извне любого пользовательского модуля, поэтому main всегда pub.

Правило «по умолчанию приватно» стоит того, чтобы ему следовать. Помечайте pub только то, что должно быть интерфейсом; остальное пусть остаётся внутри модуля.

Возвращаемые типы

Каждая функция объявляет возвращаемый тип после ->. Распространённые:

fun answer() -> i32 { return 42 }
fun ok()     -> bool { return true }
fun label()  -> String { return "ready\n" }
fun nothing() -> Void { }

Void — это тип возвращаемого значения для функции, которая делает свою работу через побочные эффекты, а не возвращая значение. Функция с типом Void не требует явного return — нормально просто упасть с конца тела.

fun log(world: World, message: String) -> Void raises {
    check world.out.write(message)
}

Вызовы, отбрасывающие значение

Если функция возвращает значение, а вам оно не нужно, вы всё равно обязаны признать возврат. Идиома — привязать его через let:

ignored — это привязка, которую остальная функция никогда не читает. Соглашение использовать имя ignored (или _) сигнализирует, что отбрасывание намеренное. Это больше трения, чем тихий выброс возвращаемого значения, и это специально: в языке, в котором код читают и генерируют агенты, непрочитанное значение часто оказывается багом, который стоит вынести на поверхность.

Роль raises

Функция, которая может упасть, объявляет это в сигнатуре. Мы видели это на main:

pub fun main(world: World) -> Void raises {
    check world.out.write("hello\n")
}

Clause raises может быть голым (любая ошибка) или конкретным:

fun validate(ok: Bool) -> i32 raises { InvalidInput } {
    if ok == false {
        raise InvalidInput
    }
    return 42
}

raises { InvalidInput } означает «эта функция может упасть только с InvalidInput, и ничем другим». Вызывающие обязаны использовать check (или более развитую форму обработки), чтобы пробросить или обработать ошибку.

Raises и Check разбирает это подробно, включая то, что происходит с несколькими типами ошибок и как check взаимодействует с clause raises у вызывающего.

Дженерик-функции

Если хотите, чтобы функция работала более чем с одним типом, объявите параметры типа в угловых скобках:

fun makePair<T, U>(left: T, right: U) -> Pair<T, U> {
    return Pair { left: left, right: right }
}

T и U — это параметры типа; вызывающий решает, чем они будут. Вызов makePair(40, 2_u8) даст вам Pair<i32, u8>. См. Generics — там разобрано всё, включая дженерик-shapes и ограничения.

Где живут функции

В маленькой программе вы пишете функции прямо в файле .0. В пакете функции распределены по файлам в src/, а компилятор разрешает межфайловые ссылки за вас. Основа остаётся той же — fun, параметры, возвращаемый тип, тело — независимо от того, где физически живёт функция.

Стиль

Несколько соглашений, которые встретятся в официальных примерах:

  • Имена функций в нижнем регистре, слова слитно (makePair) или через camelCase. Стандартная библиотека склоняется к camelCase.
  • Одно возвращаемое значение на функцию. Если нужно вернуть несколько вещей — соберите для них небольшой shape. Это понятнее, чем возвращать tuple-of-pair-of-tuples.
  • Void-функции делают только вызовы check; функции, вычисляющие значение, по возможности избегают I/O. Это разделение частично культурное, частично обеспеченное языком — чистая вычислительная функция не принимает world и потому в буквальном смысле не может делать I/O.

На последнем пункте стоит остановиться. Поскольку I/O живёт за capability World, а World передаётся явно, по сигнатуре функции видно, может ли она делать I/O. Функции, в сигнатуре которых нет World, чисты относительно внешнего мира. На это свойство могут полагаться и агенты, и люди, не читая тело.

Дальше: If/Else

Вы уже видели if мельком — следующая статья разбирает выражения if/else подробно, включая то, как они взаимодействуют с привязками и чего тут нет специально (никакой truthy-коэрсии, никакого тернарника).

Часто задаваемые вопросы

Как объявить функцию в Zero?

Используйте fun: fun name(param: Type) -> ReturnType { body }. Добавьте pub впереди, чтобы сделать функцию видимой за пределами модуля. Добавьте raises после возвращаемого типа, если функция может упасть. Например: pub fun double(value: i32) -> i32 { return value * 2 }.

Что делает ключевое слово pub?

pub помечает объявление как публичное — видимое коду за пределами текущего модуля. Без pub функция приватна для файла (или пакета), в котором объявлена. Привычная точка входа pub fun main обязана быть публичной, чтобы рантайм мог её найти и вызвать.

Как вернуть значение из функции в Zero?

Напишите return value в теле функции. Выражение должно совпадать с объявленным возвращаемым типом. Функция с возвращаемым типом Void ничего не возвращает и не требует явного return — нормально просто упасть с конца тела.

Могут ли функции Zero принимать несколько параметров?

Да. Перечислите их в скобках через запятую, каждый с именем и типом: fun add(a: i32, b: i32) -> i32 { return a + b }. Каждый параметр — это let-подобная привязка в теле функции. Zero требует явных типов на параметрах — на объявлении функций вывода типов параметров нет.

Что означает raises в сигнатуре функции?

raises объявляет, что функция может упасть. Голый raises разрешает любой тип ошибки; raises { InvalidInput } ограничивает её конкретной именованной ошибкой. Вызывающие обязаны использовать check (или другую конструкцию для fallibility), чтобы подтвердить возможность отказа — они не могут молча его проигнорировать.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ