Даже после успешной проверки программа во время выполнения создаёт значения, вывод и возможные ошибки. Начнём с того, что видит пользователь: напечатанного текста, вычисленных значений, порядка коллекций и явной ошибки выполнения.
Наблюдаем обычные значения
Сохраните следующий код как runtime-behavior.tpz:
let values = [10, 20]
print("int: {7 / 2} {-7 % 3}")
print("float: {1.5 + 1.5}")
print("safe: {values.get(9)}")Запустите его командой topaz run runtime-behavior.tpz:
int: 3 -1
float: 3.0
safe: Noneprint принимает только string. Перед вызовом интерполяция преобразует
целое число, число с плавающей точкой и Option в отображаемый текст. В
Prelude нет отдельной открытой функции println; используйте точную функцию
print, показанную здесь.
int — знаковое 64-разрядное целое. Целочисленное деление усекает к нулю, а
остаток получает знак делимого. Переполнение во время выполнения и деление на
ноль вызывают ошибку, а не циклическое переполнение. float соответствует
IEEE-754 binary64 и подчиняется правилам арифметики с плавающей точкой, а не
десятичной арифметики.
values.get(9) возвращает None: отсутствие элемента является нормальным
исходом безопасного поиска.
Наблюдаем нарушение контракта
Сравните безопасный поиск с прямым обращением по индексу:
let tasks = ["Run first program", "Build application"]
print(tasks[2])topaz run intentional-fault.tpz завершается с ненулевым кодом и указывает
место в исходном файле:
error[TPZ4001]: index 2 is out of bounds for an array of length 2
--> intentional-fault.tpz:2:7
|
2 | print(tasks[2])
| ^^^^^^^^
Ошибка выполнения — не None, не Err и не другое значение. Она прерывает
вычисление, и её нельзя перехватить постфиксным ?, операторами необязательных
значений или concurrent. Заведомо неверная константная арифметика может быть
отклонена ещё при проверке; ошибка выполнения возникает, когда программа
достигает неверного условия во время запуска.
Выбираем наблюдаемую модель неудачи
| Ситуация | Что использовать или ожидать |
|---|---|
| Отсутствие значения является нормальным | Option, например None из get |
| Вызывающий код может отреагировать на причину | Result с Ok или Err |
| Выполняемый код нарушил контракт среды | Ошибка и ненулевой код процесса |
| Исходный код неверен до запуска | Статическая диагностика topaz check |
Прямое обращение к массиву, деление целых или вычисление остатка с нулевым
делителем, переполнение целого, динамический нулевой шаг диапазона,
динамическая отрицательная целая степень и промах match во время выполнения
являются указанными источниками ошибок. Если отсутствие входит в нормальную
постановку задачи, предпочитайте безопасную библиотечную операцию.
Что остаётся устойчивым
Строки сравниваются как точные последовательности скаляров Unicode без неявной нормализации. Массивы сохраняют порядок элементов. Снимок ключей Map следует порядку вставки ключей, а операции коллекций — порядку, указанному в своём контракте. Номинальные значения сохраняют имя конструктора, когда этого требует каноническое отображение.
Реализации могут иметь разные внутренние представления лишь при одинаковом видимом результате; иначе цель должна явно отклонить программу. Это правило не превращает внутренние вспомогательные функции цели в открытый API Топаза.
Что намеренно не задано
Не следует предполагать циклическое переполнение целых по правилам среды, порядок хеш-таблицы среды, зависимое от локали форматирование чисел, неявную нормализацию Unicode, перехватываемые ошибки выполнения, очистку после аварийного завершения, неявные права на файловую систему или сеть и гарантии производительности. Порядок конкурентных задач и смешивание их вывода также намеренно не определены.
Модель значений разобрана на странице Null, Option, Result и ошибки, тайм-ауты и границы порядка — на странице Конкурентность, а точная повседневная поверхность — в разделе Базовая библиотека.