Основы синтаксиса Топаза

Освойте синтаксис Топаза - языка, где код становится поэзией. Научитесь писать красивый код с современным, выразительным синтаксисом, созданным для максимальной ясности.

"Язык, где код становится поэзией" - синтаксис Топаза стремится к максимальной выразительности при минимальном количестве кода. Освойте прекрасный синтаксис Топаза в этом полном руководстве. 🌟

🎯 Основная философия

"Писать меньше, выражать больше"

Даже сложную логику можно выразить кратко и читаемо.

Глобальный синтаксис, локальное выражение

  • Ключевые слова: Унифицированы на английском (function, let, match, case)
  • Идентификаторы: Свободное использование любого языка, русского, Unicode и даже эмодзи

Всё является выражением

Все конструкции типа if, match, for, try возвращают значения.

📝 Базовый синтаксис

Объявление переменных

// Неизменяемые переменные (по умолчанию)
let имя = "Алексей"
let возраст = 25
let активен = true

// Изменяемые переменные
mut let очки = 85
очки = 90  // Можно изменить

// Указание типа (опционально)
let расстояние: float = 3.14
let пользователи: Array<string> = ["Иван", "Мария"]

Присваивание объединения с null (??=)

Оператор‑заявление для инициализации, когда цель — None/null.

let mut name: Option<string> = None
name ??= Some("guest")

Определение функций

// Базовая функция
function приветствие(имя: string) -> string {
    "Привет, {имя}!"
}

// Параметры по умолчанию
function вычислить(x: int, y: int = 10) -> int {
    x + y
}

// Множественные возвращаемые значения
function получитьКоординаты() -> (int, int) {
    (100, 200)
}

// Функции высшего порядка
function преобразовать(данные: Array<int>, функция: (int) -> int) -> Array<int> {
    данные.map(функция)
}

Условные выражения

// if выражение (возвращает значение)
let сообщение = if возраст >= 20 {
    "Вы взрослый"
} else if возраст >= 13 {
    "Вы подросток"
} else {
    "Вы ребёнок"
}

// В стиле тернарного оператора
let статус = if онлайн { "Подключён" } else { "Отключён" }

Сопоставление с образцом

// Базовое сопоставление
let результат = match значение {
    case 1 => "Один"
    case 2 => "Два"
    case 3 => "Три"
    case _ => "Другое"
}

// Сопоставление диапазонов
let оценка = match балл {
    case 90..100 => "Отлично"
    case 80..<90 => "Хорошо"
    case 70..<80 => "Удовлетворительно"
    case _ => "Неудовлетворительно"
}

// Структурное сопоставление
let скидка = match клиент {
    case { уровень: "VIP", сумма } if сумма > 1000000 => 0.3
    case { уровень: "VIP" } => 0.2
    case { датаРегистрации } if сегодня - датаРегистрации > 365.дней => 0.1
    case _ => 0.0
}

#### Списковые паттерны

```rust
match xs {
  case [head, ..tail] => useHead(head, tail)
  case [..init, last] => useLast(init, last)
  case [a, .., z] => useEnds(a, z)
  case [x, y, ..rest] if rest.length > 0 => handle(x, y, rest)
}

Сквозной пример (guard + вложенный списковый паттерн):

let описание = match числа {
  case [] => "пусто"
  case [один] => "один"
  case [первый, .., последний] if первый < последний => "восходящий край"
  case [первый, ..середина, последний] => "края {первый}..{последний}, середина={середина.length}"
}
## 🌊 Конвейерные операции

### Базовый конвейер

```rust
// Данные текут как поэзия
let результат = исходныеДанные
    |> нормализовать()
    |> фильтровать(x => x > 0)
    |> преобразовать(x => x * 2)
    |> сортировать()

Смешивание с цепочкой методов

let обработаннаяСтрока = "привет мир"
    .split(" ")
    |> каждому(слово => слово.заглавная())
    .join(" ")
    |> добавить("!", _)
// Результат: "Привет Мир!"

Сахар для конвейера

v4

Короткие формы справа от |>:

value
  |> .length                  // сахар свойства: (x => x.length)
  |> replace("foo", _, "bar") // сахар вызова: (x => replace("foo", x, "bar"))

// Несколько `_` связываются слева-направо в ближайшем вызове
value |> format("id=", _, ":", _)

🧠 Система типов

Вывод типов

// Типы автоматически выводятся
let число = 42               // int
let текст = "привет"         // string
let массив = [1, 2, 3, 4]    // Array<int>
let объект = {               // { имя: string, возраст: int }
    имя: "Алексей",
    возраст: 25
}

Литеральные типы

// Ограничение типов точными значениями
type СветофорЦвет = "красный" | "жёлтый" | "зелёный"
type БросокКубика = 1 | 2 | 3 | 4 | 5 | 6
type КодСостояния = 200 | 404 | 500

function обработатьСигнал(цвет: СветофорЦвет) {
    match цвет {
        case "красный" => стоп()
        case "жёлтый" => осторожно()
        case "зелёный" => ехать()
        // Компилятор проверяет, что все случаи обработаны!
    }
}

Объединённые типы

type ПользовательскийВвод = string | int | null

function обработать(ввод: ПользовательскийВвод) -> string {
    match ввод {
        case с: string => "Строка: {с}"
        case н: int => "Число: {н}"
        case null => "Нет ввода"
    }
}

🔁 Циклы

Циклы for (выражения)

// Итерация по диапазону
for i in 1..10 {
    print("Число: {i}")
}

// Итерация по массиву
for элемент in ["яблоко", "банан", "вишня"] {
    print("Фрукт: {элемент}")
}

// С индексом
for (индекс, значение) in данные.enumerate() {
    print("{индекс}: {значение}")
}

// for тоже возвращает значения!
let квадраты = for x in 1..5 { x * x }  // [1, 4, 9, 16]

Шаг диапазона с by

v4
// Шаг вперёд
for i in 0..10 by 2 { print(i) }         // 0,2,4,6,8,10

// Шаг назад (отрицательный шаг)
for i in 10..0 by -3 { print(i) }        // 10,7,4,1

// Шаг по датам/времени (библиотечные длительности)
for day in startDate..endDate by 1.day { расписание.добавить(day) }

Циклы while

mut let счётчик = 0
while счётчик < 10 {
    print("Счётчик: {счётчик}")
    счётчик = счётчик + 1
}

📚 Коллекции

Массивы

// Базовые массивы
let числа = [1, 2, 3, 4, 5]
let фрукты = ["яблоко", "банан", "вишня"]

// Методы массивов
let большие = числа.filter(x => x > 2)   // [3, 4, 5]
let квадраты = числа.map(x => x * x)     // [1, 4, 9, 16, 25]
let сумма = числа.reduce(0, +)           // 15

Принадлежность (in)

// Массивы/Списки/Множества
let ok1 = 3 in [1, 2, 3]
let ok2 = "a" in Set.of("a", "b")

// Ключи map
let hasId = "id" in userMap.keys

// Диапазоны
let inside = 5 in 1..10
let outside = 10 in 1..<10

Объекты/Структуры

// Литерал объекта
let пользователь = {
    имя: "Алексей",
    возраст: 25,
    email: "aleksey@example.com"
}

// Определение структуры
struct Пользователь {
    имя: string,
    возраст: int,
    email: string
}

// Экземпляр структуры
let новыйПользователь = Пользователь {
    имя: "Мария",
    возраст: 30,
    email: "maria@example.com"
}

Литерал обновления записи

v4

Немутируемое обновление: мелкое копирование записи с изменением выбранных полей.

let user = { name: "Alice", age: 20, city: "Seoul" }
let updated = user{ age: user.age + 1, city: "Busan" }
// { name: "Alice", age: 21, city: "Busan" }

🎭 Строковые шаблоны

Базовая интерполяция

let имя = "Алексей"
let возраст = 25
let приветствие = "Привет, {имя}! Вам {возраст} лет."

Тегированные шаблоны

v4

Стандартизированные теги с сохранением метаданных и безопасностью:

let путь = p"/home/{user}/docs/{fileName}"         // нормализация пути
let шаблон = r"^[a-z0-9_]+$"                        // регулярные выражения с лёгкими экранированиями
let команда = sh"grep {шаблон} {файл}"              // безопасный shell‑шаблон (политики выполнения)

// SQL: параметры всегда привязываются (никакой прямой вставки строк)
let запрос = sql"SELECT * FROM users WHERE age > {возраст} AND city = {город}"

⚡ Асинхронная обработка

Автоматическая асинхронность

// I/O автоматически асинхронный, но пишется как синхронный!
function получитьИнфоПользователя(id: int) -> Пользователь {
    let основнаяИнфо = API.получитьПользователя(id)      // авто async обработка
    let профиль = API.получитьПрофиль(id)               // авто async обработка
    
    Пользователь {
        основнаяИнфо: основнаяИнфо,
        профиль: профиль
    }
}

Параллельное выполнение

// Запуск нескольких задач параллельно
let результаты = concurrent {
    погода: ПогодаAPI.текущая("Москва"),
    курс: ВалютаAPI.курсДоллара(),
    новости: НовостиAPI.заголовки(5)
}

🛡️ Обработка ошибок

Тип Result

function безопасноеДеление(a: int, b: int) -> Result<int, string> {
    if b == 0 {
        Err("Нельзя делить на ноль")
    } else {
        Ok(a / b)
    }
}

// Просто с оператором ?
function сложноеВычисление(x: int) -> Result<int, string> {
    let результат1 = безопасноеДеление(x, 2)?
    let результат2 = безопасноеДеление(результат1, 3)?
    Ok(результат2 * 10)
}

Выражение try

let конфигурация = try {
    прочитатьФайл("config.json")?
} else {
    // Вернуть значение по умолчанию
    { тема: "тёмная", язык: "русский" }
}

🎨 Продвинутые возможности

Частичное применение

let добавить10 = добавить(10, _)
let результат = [1, 2, 3].map(добавить10)  // [11, 12, 13]

Опциональная безопасная цепочка (?.)

Используйте ?. только когда слева Option<T> или объединение с null (например, T | null). Оператор сводится к Option.map/flatMap и выполняется слева направо с коротким замыканием.

let user: Option<{ name: string, profile: Option<{ city: string }> }> =
    Some({ name: "Ann", profile: Some({ city: "Seoul" }) })

let nameOpt = user?.name
let cityOpt = user?.profile?.city

// С комбинированием с ?? для значений по умолчанию
let city = user?.profile?.city ?? "Unknown"

// Это не общий объектный чейнинг — для обычных значений используйте `.`
let plain = { name: "Jo" }
let ok = plain.name

Эквивалентность с map/flatMap:

// user?.profile?.city
let viaFns = user
  |> Option.flatMap(_, u => u.profile)
  |> Option.map(_, p => p.city)

Макросы

// Макрос повторного выполнения
macro повторитьВыполнение(раз: int) {
    for i in 1..раз {
        print("Выполнение {i}/{раз}")
        yield  // Выполнить пользовательский код
    }
}

повторитьВыполнение(3) {
    важнаяЗадача()
}

🔧 Практические примеры

Вызов веб-API

let данныеПользователя = fetch("https://api.example.com/users/1")
    |> json()
    |> (данные => {
        имя: данные.name,
        email: данные.email,
        датаРегистрации: Date.parse(данные.created_at)
    })

Конвейер обработки данных

let результатАнализа = CSV.прочитать("продажи.csv")
    |> фильтр(строка => строка.выручка > 1000000)
    |> группировать(строка => строка.регион)
    |> агрегировать(группа => {
        регион: группа.ключ,
        общаяВыручка: группа.значения.сумма(строка => строка.выручка),
        средняяВыручка: группа.значения.среднее(строка => строка.выручка)
    })
    |> сортировать(поУбыванию: строка => строка.общаяВыручка)

🎯 Следующие шаги

Теперь вы освоили базовый синтаксис Топаза! Что изучать дальше:

Продолжайте своё путешествие, где программирование становится поэзией с Топазом!