Примечание о библиотечных именах: вспомогательные функции и вызовы методов вне минимума стандартной библиотеки (
файлСуществует,прочитатьСодержимоеФайла,fetch,.ok,.json,.status,toUpperCase,Dateи т.д.) являются иллюстративными заполнителями, а не каноническими API.
Топаз сочетает статическую систему типов с выводом типов. Код остаётся безопасным и при этом кратким. Компилятор автоматически выводит типы, когда вы их не указываете, но при необходимости можно быть явным.
Примитивные типы
Целые числа и вещественные
// Целое число
let возраст: int = 25
let большойСчётчик: int = 9999999999
// Число с плавающей точкой
let цена: float = 19.99
let пи: float = 3.141592653589793
// Вывод типов
let счёт = 95 // выводится как int
let среднее = 87.5 // выводится как floatПримитивы Topaz:
int(64-битное знаковое),float(IEEE-754 binary64),string,boolи(). При этом()также является выражением: единственным значением юнит-типа. Целые числа произвольной точности не специфицированы в Topaz v5.2, аint/floatникогда не смешиваются неявно в арифметике.
Булев тип
let активен = true
let естьПраваАдмина = false
// Использование в условиях
if активен {
print("Сервис активен")
}Строки
// Строка (один символ или более длинный текст)
let оценка: string = "A"
let эмодзи: string = "😀"
let имя = "Иван Топаз"
let длинноеПредложение = "Добро пожаловать в Топаз — один способ сказать!"
// Интерполяция строк
let приветствие = "Привет, {имя}! Хорошая сегодня погода."
print(приветствие) // "Привет, Иван Топаз! Хорошая сегодня погода."Строки в одинарных кавычках и символьные литералы (
'A','') не являются каноническим синтаксисом Topaz. Используйте строки в двойных кавычках и для одного символа, и для более длинного текста. Строки не индексируются и не предоставляют.length. Доступ на уровне скаляров идёт черезs.scalars().
Типы коллекций
Массивы
// Объявление и инициализация массива
let mut числа: Array<int> = [1, 2, 3, 4, 5]
let имена = ["Алиса", "Боб", "Чарли"] // выводится как Array<string>
// Манипуляции с массивом
let первый = числа[0] // 1 — прямая индексация даёт фолт вне границ
let безопасно = числа.get(0) // Some(1) — чтение без фолта, возвращает Option<int>
числа.push(6) // [1, 2, 3, 4, 5, 6]
let длина = числа.length // 6
// Операции в функциональном стиле
let квадраты = map(числа, x => x * x)
let чётные = filter(числа, x => x % 2 == 0)Записи
// Определение записи
let пользователь = {
имя: "Иван Топаз",
возраст: 28,
почта: "ivan@example.com",
активен: true
}
// Доступ к свойствам
print(пользователь.имя) // "Иван Топаз"
// Неизменяемое обновление через record literal
let калькулятор = { значение: 0 }
let послеСложения = калькулятор{ значение: калькулятор.значение + 10 }
let послеУмножения = послеСложения{ значение: послеСложения.значение * 2 }
print("{послеУмножения.значение}") // 20
print("{калькулятор.значение}") // 0 (исходное значение не меняется)
// Переиспользуемое поведение можно оформить обычными функциями
function добавитьККалькулятору(состояние: { значение: int }, x: int) -> { значение: int } {
состояние{ значение: состояние.значение + x }
}
function умножитьКалькулятор(состояние: { значение: int }, x: int) -> { значение: int } {
состояние{ значение: состояние.значение * x }
}Продвинутые типы
Тип Option
// Тип Option для безопасности от null
let результатПоиска: Option<string> = None
let имяПользователя: Option<string> = Some("admin")
// Безопасная обработка с сопоставлением образцов
match имяПользователя {
case Some(имя) => print("Добро пожаловать, {имя}!")
case None => print("Требуется авторизация")
}
// Безопасная обработка с ?. и ?? (строки не предоставляют .length;
// число скаляров считается через s.scalars())
let длинаИмени = имяПользователя?.scalars()?.length ?? 0
let отображение = имяПользователя ?? "guest"Тип Result
// Тип Result для обработки ошибок
function прочитатьФайл(путь: string) -> Result<string, string> {
if файлСуществует(путь) {
Ok(прочитатьСодержимоеФайла(путь))
} else {
Err("Файл не найден")
}
}
// Безопасная обработка ошибок
match прочитатьФайл("data.txt") {
case Ok(содержимое) => print("Содержимое файла: {содержимое}")
case Err(сообщениеОшибки) => print("Произошла ошибка: {сообщениеОшибки}")
}Объединённые типы (Union Types)
// Тип, который может быть одним из нескольких типов
type ID = int | string
type Статус = "ожидание" | "обработка" | "завершено" | "ошибка"
let идПользователя: ID = 12345
let статусЗадачи: Статус = "обработка"
// Обработка для конкретного типа с сопоставлением образцов
function показатьID(id: ID) -> string {
match id {
case числовой: int => "Числовой ID: {числовой}"
case текстовый: string => "Строковый ID: {текстовый}"
}
}Использование дженерик типов
let числа: Array<int> = [1, 2, 3]
let имяОпционально: Option<string> = Some("Alice")
let результатПарсинга: Result<Пользователь, string> = разобратьПользователя(rawJson)
let кеш: Map<string, int> = Map.new()Дженерик-функции и псевдонимы
Topaz поддерживает дженерик-объявления функций ранга 1 и дженерик-псевдонимы
типов. Параметры типов выводятся в каждой точке вызова; явные аргументы
типов вроде f<int>(x) не входят в Topaz v5.2. Фиксируйте тип
аннотированной привязкой, параметром или позицией возврата.
function первыйЭлемент<T>(xs: Array<T>) -> Option<T> {
match xs {
case [] => None
case [первый, ..] => Some(первый)
}
}
let первый: Option<int> = первыйЭлемент([1, 2, 3])
type Пара<T> = { первый: T, второй: T }
let границы: Пара<int> = { первый: 0, второй: 100 }Границы дженериков, ограничения и интерфейсы остаются отложенными.
Псевдонимы типов
// Даём простые имена сложным типам
type ИнфоПользователя = {
имя: string,
возраст: int,
почта: string,
разрешения: Array<string>
}
type ФункцияОбратногоВызова = (string) -> ()
// Пример использования
let администратор: ИнфоПользователя = {
имя: "Админ Пользователь",
возраст: 35,
почта: "admin@topaz.ooo",
разрешения: ["чтение", "запись", "удаление"]
}Преобразование типов
let строковоеЧисло = "123"
// toInt возвращает Option<int> — обрабатывайте случай None явно
let разобрано = match toInt(строковоеЧисло) {
case Some(значение) => значение
case None => 0
}Стандартная библиотека Topaz определяет
toInt(text: string) -> Option<int>как canonical helper для преобразования; он никогда не падает молча. Другие формы преобразования из legacy-примеров (float(...),string(...),tryInt(...)) являются иллюстративными placeholder, а не каноническими API.
Продвинутые паттерны
Деструктурирующее присваивание
// Деструктурирование массива
let [первый, второй, ..остальные] = [1, 2, 3, 4, 5]
print("{первый}") // 1
print("{остальные}") // [3, 4, 5]
// Деструктурирование объекта
let { имя, возраст } = пользователь
print("Имя: {имя}, Возраст: {возраст}")
// Деструктурирование внутри тела функции
function поприветствовать(пользователь: { имя: string, возраст: int }) {
let { имя, возраст } = пользователь
print("Привет, {имя}! Вам {возраст} лет.")
}Защитники типов
Topaz сужает типы через
match+ TypePattern, а не черезis/as/any. Типanyи операторыis/asне являются каноническим синтаксисом Topaz. Примеры narrowing через match будут на странице управления потоком.
Практическое применение
// Практические определения типов для реальных проектов
type Пользователь = {
id: int,
имя: string,
почта: string,
датаСоздания: string
}
type ОтветПользователя = {
успех: bool,
данные: Option<Пользователь>,
ошибка: Option<string>,
кодСостояния: int
}
// API получения пользователя
function получитьПользователя(id: int) -> ОтветПользователя {
let ответ = fetch("/api/users/{id}")
if ответ.ok {
let данныеПользователя = ответ.json()
return {
успех: true,
данные: Some(данныеПользователя),
ошибка: None,
кодСостояния: ответ.status
}
} else {
return {
успех: false,
данные: None,
ошибка: Some("Пользователь не найден"),
кодСостояния: ответ.status
}
}
}Система типов Топаза совмещает безопасность с малой и предсказуемой поверхностью. Вывод типов позволяет писать краткий код; там, где нужно, точные аннотации типов отлавливают ошибки.