Структура программы

Модули и видимость

Соедините два файла импортами и экспортами, выберите корень и исправьте ошибки приватных имён и циклов.

Разделите программу на файлы и выберите имена, доступные из других файлов. Вы запустите учебный планировщик из двух файлов и разберёте ошибки доступа к приватным именам и циклического импорта.

Установите topaz по руководству Первая программа. Каждый файл .tpz является модулем. Отдельное объявление module не требуется.

Запустите программу из двух файлов

В рабочей папке, где ещё нет проекта study-plan, выполните следующие команды.

BASH
topaz init --root study-plan
cd study-plan

init создаёт topaz.toml и src/main.tpz. Сохраните созданный манифест. Добавьте src/plan.tpz и замените src/main.tpz кодом ниже. После создания файла блокировки структура будет следующей.

study-plan/
├── topaz.toml
├── topaz.lock
└── src/
    ├── main.tpz
    └── plan.tpz

Сохраните данные задач и функции в src/plan.tpz.

TOPAZ
export record УчебнаяЗадача {
    название: string,
    минуты: int,
    выполнена: bool = false,
}

function разобратьМинуты(текст: string) -> Result<int, string> {
    match toInt(текст) {
        case Some(значение) if значение > 0 => Ok(значение)
        case _ => Err("minutes must be a positive integer")
    }
}

export function создатьЗадачу(
    название: string,
    текстМинут: string,
    выполнена: bool = false,
) -> Result<УчебнаяЗадача, string> {
    let минуты = разобратьМинуты(текстМинут)?
    Ok(УчебнаяЗадача { название: название, минуты: минуты, выполнена: выполнена })
}

export function подвестиИтог(задачи: Array<УчебнаяЗадача>) -> string {
    let mut осталось = 0
    for задача in задачи {
        if !задача.выполнена {
            осталось = осталось + задача.минуты
        }
    }
    "tasks={задачи.length}, remaining={осталось} minutes"
}

В src/main.tpz импортируйте три имени, используемые приложением.

TOPAZ
import src.plan { УчебнаяЗадача, создатьЗадачу, подвестиИтог }

export function main(аргументы: Array<string>, ввод: string) -> Result<int, string> {
    let задачи: Array<УчебнаяЗадача> = [
        создатьЗадачу("Run first program", "10", true)?,
        создатьЗадачу("Build application", "25", false)?,
    ]
    print(подвестиИтог(задачи))
    Ok(0)
}

Выполните команды внутри study-plan, рядом с topaz.toml.

BASH
topaz lock --root .
topaz check --root . --locked
topaz run --root . --locked

Команда lock создаёт topaz.lock. Проверка завершается с кодом 0, а программа печатает итог.

Вывод
tasks=2, remaining=25 minutes

Путь src.plan указывает на src/plan.tpz внутри выбранного корня. Компилятор проверяет входной файл и импортируемые модули, следуя и по их импортам. Эти файлы образуют единицу компиляции. Файлы, не связанные с этой точкой входа, не проверяются.

Выберите имена для экспорта

Объявление по умолчанию приватно. Добавьте export, чтобы использовать его из другого модуля. Здесь экспортированы УчебнаяЗадача, создатьЗадачу и подвестиИтог. Функция разобратьМинуты остаётся приватной.

Если src/main.tpz попытается импортировать или вызвать разобратьМинуты, проверка завершится ошибкой. Вызывайте экспортированную создатьЗадачу. Если вспомогательную функцию нужно открыть, добавьте export к её объявлению в src/plan.tpz. Импортированные привязки доступны для чтения без возможности изменения.

ЗадачаФорма импорта
Выбрать именаimport src.plan { УчебнаяЗадача, создатьЗадачу }
Задать псевдоним выбранного имениimport src.plan { подвестиИтог as показатьИтог }
Сгруппировать имена под префиксомimport src.plan as план

Последняя форма создаёт пространство имён, то есть префикс для поиска экспортированных имён при компиляции. Через него можно вызвать план.создатьЗадачу(...). Само пространство имён не является значением во время выполнения.

В аннотации типа можно написать план.УчебнаяЗадача. Чтобы создать УчебнаяЗадача { ... }, импортируйте УчебнаяЗадача напрямую. Форма план.УчебнаяЗадача { ... } отклоняется. Если вы написали import src.plan as план { подвестиИтог }, замените её одной из отдельных форм импорта выше.

Найдите файлы от правильного корня

Сопоставьте путь с точками с точными именами файлов и папок под корнем. Располагайте импорты в начале файла, перед остальными элементами.

Команды пакета читают точку входа, зависимости и цель сборки из topaz.toml. При явном указании файла его папка становится корнем по умолчанию. Параметр --root позволяет выбрать содержащую его папку. Если src.plan не найден, проверьте --root и расположение src/plan.tpz.

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

Разорвите цикл импортов

В отдельной папке сохраните этот код как a.tpz.

import b

export function изА() -> int {
    1
}

Рядом сохраните следующий код как b.tpz.

import a

export function изБ() -> int {
    2
}

В этой папке команда topaz check a.tpz --root . завершается с ненулевым кодом и сообщает о цикле.

Вывод
error[TPZ3006]: import cycle: a -> b -> a
 --> a.tpz:1:8
  |
1 | import b
  |        ^

a.tpz: 1 diagnostic

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

Формы, которые компилятор отклоняет

Компилятор отклоняет use, строковые и шаблонные пути, псевдоним вместе со списком выбора и импорт ради побочного эффекта. Также отклоняются списки экспорта, групповой и повторный экспорт, переименование при экспорте и export let mut. Путь импорта не может выходить за корень, а пространство имён не открывает приватные имена.

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