Шаг 8. Эффекты
Зачем#
Если функция называется total(items), мы ожидаем, что она посчитает сумму. Но может ли она заодно сходить в сеть, прочитать файл
или уснуть на секунду? В большинстве языков это нельзя узнать, не прочитав тело. В OxL это видно в сигнатуре:
| Эффект | Разрешает |
|---|---|
io | файлы, процессы, окружение, stdout/stderr |
net | сеть: rpc, HTTP, акторы, кластер |
db | ORM, std.kv, встроенное хранилище |
time | чтение часов, sleep, таймеры |
rand | случайные числа |
pure | ничего из перечисленного |
Функция может вызывать только функции, чьи эффекты входят в её собственный набор. Поэтому pure-функции легко тестировать
(результат зависит только от аргументов), а подозрительные места — где что-то «просачивается» — находит компилятор. Те же эффекты
говорят планировщику, что функцию можно приостановить, поэтому в OxL нет async/await и «цвета» функций.
Пример#
fn double(x: i32) -> i32 pure => x * 2 // pure: нет ни ввода-вывода, ни времени
fn log(msg: str) io { // io: пишет в stdout
print("[log] {msg}")
}
fn timed(x: i32) -> i32 io, time { // несколько эффектов через запятую
log("sleeping")
sleep(5ms)
double(x) // pure-функцию можно вызывать откуда угодно
}
fn main() io, time {
print("{timed(21)}")
}[log] sleeping 42
Разбор#
double—pure: вызывается откуда угодно, включая другиеpure-функции.logобъявляетio(в-> ()форме тип результата опускается).timedперечисляетio, time: внутри она вызывает иlog, иsleep.mainтоже обязан объявитьio, time.- У приватных функций эффекты можно не писать — компилятор выведет их. У
pub(api)функций эффекты обязательны: это часть публичного API, и изменение эффектов — ломающее изменение. - Паника эффектом не считается: она возможна в любой функции.
Типичная ошибка: эффект не объявлен#
fn calc() -> i32 pure {
sleep(1s) // sleep требует эффекта time
42
}effects
effects: calc объявлена pure, но вызывает sleep, а ему нужен time. Исправить можно двумя способами:
добавить эффект в сигнатуру (-> i32 time) и объявить его у всех вызывающих — или убрать sleep из функции, которая должна
оставаться чистой. Второй способ обычно лучше: держите бизнес-логику pure, а ввод-вывод — на краях программы.
Что запомнить#
- Эффекты — часть типа функции;
pure— отсутствие эффектов. - Вызывающий должен «позволять» все эффекты вызываемого.
- Чистое ядро + тонкая оболочка с эффектами — естественная архитектура на OxL.
Дальше: модули и пакеты.