Основы языка

Типы

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

Типы позволяют проверяющему ответить до запуска программы на практический вопрос: какие значения разрешено передавать через эту границу? Для большинства локальных значений тип писать не нужно. Аннотация полезна там, где она поясняет важный вход, результат или выбор предметной области.

Начните с одной проверяемой программы

Сохраните этот код в файле types.tpz:

TOPAZ
type Stage = "draft" | "ready"

function describe(stage: Stage, checks: int) -> string {
    "{stage} after {checks} checks"
}

let checks = 3
let current: Stage = "ready"
let render: (Stage, int) -> string = describe

print(render(current, checks))

Сначала проверьте границы типов, затем запустите программу:

BASH
topaz check types.tpz
topaz run types.tpz

Результат:

Вывод
ready after 3 checks

Прочитайте типы по коду и результату

У checks нет аннотации. Начальное значение — целое число, поэтому Топаз выводит тип int. У current аннотация есть: программе требуется именно Stage, а не любая строка string.

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

У render указан тип функции. Запись (Stage, int) -> string означает «функция принимает Stage и int, а возвращает string». Именованная функция describe имеет именно такую форму, поэтому её можно сохранить в связывании и вызвать позднее.

Названия базовых типов пишутся строчными буквами: int, float, bool, string и единичный тип (). Для контейнеров используются именованные конструкторы Array<T>, Map<K, V> и Set<T>. Ожидаемое отсутствие значения выражается через Option<T>, а восстанавливаемая ошибка — через Result<T, E>.

Выберите форму, которая передаёт намерение

ЗадачаЧто выбратьПочему
Очевидное локальное значениеВывод типаНачальное значение уже ясно показывает тип.
Публичный вход, результат или важная локальная границаАннотациюПроверяющий и читатель сразу видят задуманный контракт.
Одно значение из закрытого набора вариантовОбъединение или псевдоним объединения литераловВсе допустимые варианты перечислены явно.
Значение, которое позже будут вызыватьТип функции, например (int) -> boolТипы параметров и результата хранятся вместе с функцией.
Самостоятельная сущность предметной области, поля или именованные вариантыrecord, enum или newtypeНоминальные объявления остаются разными, даже если устроены похоже.

Псевдоним type делает код понятнее, но остаётся прозрачным. Если смешение двух похожих по форме значений было бы настоящей ошибкой, выбирайте номинальные данные. На странице Записи и номинальные данные сопоставлены создание и сопоставление для record, enum и newtype.

Дайте пустому значению контекст

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

let names: Array<string> = []
let selected: Option<int> = None

Здесь канонические формы показаны как текст. Проверяемой программой этой страницы остаётся полный пример выше.

Частая ошибка: перенос записи типов из целевого языка

Типы коллекций Топаза записываются как Array<T>, Map<K, V> и Set<T>. В типе функции параметры заключаются в круглые скобки, после чего -> отделяет тип результата. Единая запись помогает проверяющему и создаваемым целевым программам опираться на один исходный контракт.

Точные ограничения

  • Между int и float нет неявного преобразования.
  • Параметры обобщённых типов инвариантны: разные аргументы типов не становятся совместимыми лишь из-за сходства содержимого.
  • Ограничения обобщённого типа допускаются только у параметров именованной функции. Разрешённая форма первого ранга описана на странице Функции и обобщения.
  • Рекурсивные псевдонимы, аннотации вариантности, полиморфизм высших рангов и более широкая рефлексия пока отложены.
  • Если контекст так и не определил параметры пустого обобщённого значения, это статическая ошибка, а не повод угадывать тип во время выполнения.

Если вывод типов, аннотации и изменяемость ещё непривычны, вернитесь к уроку Значения и функции. Выбор между отсутствием, восстанавливаемой ошибкой и сбоем разобран на странице Null, Option, Result и ошибки.