Данные и управление

Функции и обобщения

Выберите именованную функцию, лямбду или обобщённую функцию и разберите типы параметров, результата и ограничения протоколов.

Выберите именованную функцию, лямбду или обобщённую функцию для повторяемой задачи. Пример показывает, как типы параметров и результата связывают эти формы.

Установите Топаз по инструкции Первая программа. Если объявления функций вам незнакомы, начните с урока Значения и функции.

Запустите три формы функций

Сохраните этот код в файле functions-generics.tpz в рабочей папке.

TOPAZ
record Метка derives Show {
    текст: string,
}

function применитьДважды(значение: int, шаг: (int) -> int) -> int {
    шаг(шаг(значение))
}

function первыйИли<Тип>(значения: Array<Тип>, запасное: Тип) -> Тип {
    match значения {
        case [] => запасное
        case [первое, ..] => первое
    }
}

function показатьЗначение<Тип: Show>(значение: Тип) -> string {
    Show.show(значение)
}

let увеличить: (int) -> int = значение => значение + 1
let первое: string = первыйИли(["Topaz", "Classic"], "none")
let показано = показатьЗначение(Метка { текст: "docs" })

print("{применитьДважды(3, увеличить)}")
print(первое)
print(показано)

В той же папке проверьте файл и запустите его.

BASH
topaz check functions-generics.tpz
topaz run functions-generics.tpz

Проверка завершается с кодом 0. Программа выводит следующие три строки.

Вывод
5
Topaz
Метка { текст: docs }

Передайте функцию как аргумент

применитьДважды принимает целое число и функцию, а возвращает целое число. Параметр шаг имеет тип (int) -> int, поэтому переданная функция должна принимать и возвращать int.

увеличить является лямбдой, безымянной функцией, записанной через =>. Она прибавляет единицу к входному значению. Вызов применитьДважды(3, увеличить) вычисляет увеличить(увеличить(3)) и получает 5.

Последнее выражение тела функции задаёт результат. Для раннего выхода используйте return. Лямбду можно хранить и передавать как значение, а также захватывать в ней окружающие привязки. Пример счётчика, сохраняющего значение между вызовами, есть в разделе Привязки, области видимости и замыкания.

Используйте одну функцию с разными типами

первыйИли<Тип> возвращает первый элемент массива или запасное, если массив пуст. Тип является параметром типа. Он указывает, что элементы массива, запасное значение и результат имеют один тип.

Пример передаёт массив строк, поэтому Топаз выводит Тип = string и возвращает "Topaz". Если заменить массив на [], результатом будет "none". Запасное значение по-прежнему задаёт тип string.

Это полиморфизм первого ранга. Параметры типа объявляются у именованной функции, а каждый вызов определяет их типы. Лямбда получает конкретные типы параметров из контекста. Используйте именованную обобщённую функцию, когда один алгоритм работает с разными типами, как в первыйИли.

Задайте нужную операцию ограничением

показатьЗначение вызывает Show.show, чтобы преобразовать аргумент в строку. Show является статическим протоколом, именованным набором операций, наличие реализации которых проверяет компилятор. Ограничение Тип: Show требует, чтобы тип аргумента реализовывал этот протокол.

Метка derives Show предоставляет реализацию для Метка. Это номинальное соответствие, установленное для объявленного типа. Базовый тип, структурная запись, функция или коллекция не получают такого соответствия просто из-за наличия похожей операции.

Сохраняйте Тип: Show, пока показатьЗначение вызывает Show.show. Удаление ограничения делает этот вызов статической ошибкой. Для тела, использующего оба протокола, укажите Тип: Show + JSON. Требуются оба ограничения, а порядок записи не важен.

Выберите форму функции

Что нужноЧто использовать
Повторяемая операция с известными типами параметров и результатаИменованная функция с конкретными типами
Операция для хранения или передачи как значенияЛямбда
Один алгоритм, связывающий параметры и результат для разных типовИменованная обобщённая функция
Операции протокола в обобщённом телеИменованная обобщённая функция с нужными ограничениями

Используйте значения по умолчанию и переменное число аргументов

Параметры неизменяемы. Значение по умолчанию вычисляется, когда аргумент пропущен. Оно должно быть литералом или выражением const и не может ссылаться на другой параметр.

Для переменного числа аргументов запишите последний параметр как ...args: T. Внутри функции args имеет тип Array<T>.

Правило вызоваПоведение
Вычисление функции и аргументовВызываемая функция вычисляется один раз, затем позиционные аргументы и аргументы с развёртыванием слева направо
Именованные аргументыСледуют за позиционными аргументами и аргументами с развёртыванием
Развёртывание массиваЗаполняет параметр переменной длины, но не фиксированные параметры
Явные аргументы типаУкажите полный список в порядке объявления, согласованный со значениями и ожидаемым результатом

При обобщённом вызове аргументы типа обычно выводятся автоматически. Там, где разрешено задавать их явно, неполный список вызывает статическую ошибку.

Поддерживаемые обобщённые объявления

Собственные параметры типа функции и ограничения протоколов объявляйте у именованной функции. Лямбды получают конкретные типы из контекста и не могут объявлять собственные параметры типа.

  • Лямбды, типы функций, псевдонимы, номинальные объявления, методы получателя и методы протокола не могут объявлять ограничений.
  • Обобщённое тело может использовать операции, разрешённые его объявленными ограничениями. Операция, требующая более сильного ограничения, вызывает статическую ошибку.
  • Полиморфизм высших рангов, обобщённые лямбды, значения параметров типа по умолчанию, аннотации вариантности, динамическая диспетчеризация протоколов и произвольные ограничения трейтов не поддерживаются.

Раздел Типы поможет выбрать типы параметров и результата. Перейдите к разделу Привязки, области видимости и замыкания, чтобы проследить за захваченными значениями.