Полярные типы данных в Edva

—

от автора

Полярные типы данных (Ramifications) в языке Эдва (Edva): единая замена для bool, Option и Result

В большинстве языков программирования понятия «истина/ложь», «значение есть/значения нет» и «успех/ошибка» живут в разных мирах. Для первого существует примитив bool и конструкции if/else, для второго — Option<T> (Maybe a), для третьего — Result<T, E> (Either a b). У каждого из них свои конструкторы (true/false, Some/None, Ok/Err) и свой набор комбинаторов.

Системный язык программирования Edva, спроектированный без единого ключевого слова, сводит все бинарные исходы к единому примитиву на уровне системы типов — алгебраическим разветвлениям (Algebraic Ramifications, или коротко — Rams).

Четыре формы полярного типа и представление в памяти

В Эдве любой бинарный исход описывается типом с двумя полюсами: положительным (левым) и отрицательным (правым). В зависимсти от того, несут ли полюса полезную нагрузку, существует ровно четыре формы:

Форма типа

Положительный полюс

Отрицательный полюс

Что заменяет в классических языках

<>

<+>
пусто

<->
пусто

bool (true / false)

< T | >

<+ value +>
значение типа T

<-->
пусто

Option<T> (Some(T) / None)

< | E >

<++>
пусто

<- error ->
ошибка типа E

Статус выполнения (Ok(()) / Err(E))

< T | E >

<+ value ->
значение типа T

<- error +>
ошибка типа E

Result<T, E> (Ok(T) / Err(E))

На уровне LLVM IR значения полярных типов не требуют аллокаций в куче или сложных вложенных тегов — они укладываются в компактное необёрнутое (unboxed) представление { i1, i64 } (бит полярности плюс полезная нагрузка), а в случаях, когда тип уже представляет собой указатель, не нужен даже бит полярности, поскольку второй полюс можно выразить нулевым адресом, и представление сокращается до { i64 }.

Конструкторы полюсов

Для явного создания значений полярного типа используются симметричные символьные конструкторы:

  • Положительный полюс: <+>, <+ val +>, <++>, <+ val -> (пустой успех или успех со значением val).

  • Отрицательный полюс: <->, <-->, <- err ->, <- err +> (пустая неудача или неудача со значением err).

Взаимодействие с оператором выбора и стражами | и […]

Поскольку в Edva нет ключевых слов if, else, match или switch, управление потоком строится на операторе выбора, который выглядит так:

значение [страж ветвь]+

Для обычных значений стражи в квадратных скобках проверяют равенство или предикат, а | выступает веткой по умолчанию:

  classify = score =>   score [100]    "perfect"         [99, 98] "almost perfect"         [> 90]   "distinction"         [> 50]   "pass"         |        "fail"

Когда же слева от оператора выбора | оказывается полярный тип (ramification), срабатывает позиционное сопоставление:

  • первая ветка после | обрабатывает положительный полюс (+),

  • вторая ветка после | обрабатывает отрицательный полюс (-).

Если полюс несет значение, внутри ветки к нему можно обратиться через позиционный плейсхолдер _ (или секцию оператора):

// Функция возвращает < Int | > (аналог Option<Int>)res = find_target(10, 7)// Первая ветка берет значение из <+ val +>, вторая срабатывает на <-->res | print_int(_) | print $ "Not found"

Никаких if let Some(x) = res или match res { Some(x) => ..., None => ... }: полярность значения напрямую выбирает нужную ветвь выражения.

Циклы как генераторы полярных типов (>—)

В императивных языках поиск элемента в цикле обычно требует мутабельного флага found = false или досрочного return из функции. В Edva циклы (@) являются выражениями, которые с помощью оператора выхода >-- естественным образом возвращают < T | >:

// Цикл ищет цель в диапазоне 0..count и возвращает < Int | >find_target: Int, Int => < Int | >find_target = count, target =>   count @ i =>      i [target] -->         // если i == target, прерываем цикл (break)   >--      target                 // значение при срабатывании -->

Механика работы >--:

  1. Если внутри цикла сработал оператор прерывания -->, блок после >-- вычисляет результирующее значение и автоматически упаковывает его в положительный полюс <+ target +>.

  2. Если цикл прошел до конца без прерываний, выражение целиком возвращает отрицательный полюс <-->.

Полученный результат тут же готов к передаче в оператор выбора | или дальше по конвейеру.

Композиция и обработка ошибок: $>> и —!

Когда функции возвращают < T | E > или < T | >, Edva предлагает два способа композиции без ручного разбора веток: конвейерное монадическое связывание и постфиксное пробрасывание.

1. Монадический bind ($>>)

Оператор $>> пропускает положительный полюс через цепочку функций, а при возникновении отрицательного полюса (<- err -> или <-->) коротко замыкает вычисление и пробрасывает его на выход:

find_user: String => < User | Error >find_user = id =>   query_db(id) $>> validate $>> (<+ _ +>)// выражение с подчёркиванием в скобках это лямбда, эквиалентно x => <+ x +>

Если query_db или validate вернут отрицательный полюс с ошибкой, вся цепочка немедленно завершится с этим же отрицательным полюсом типа Error: <- error ->.

2. Постфиксная распаковка и ранний возврат (—!)

Для последовательного кода, где нужно распаковать несколько полярных значений подряд, используется постфиксный оператор --!:

load_config: String => < Config | Error >load_config = path =>   raw = io::read_file(path)--!   cfg = json::parse(raw)--!   <+ cfg +>

Если io::read_file(path) возвращает <+ content +>, оператор --! извлекает content и связывает его с raw. Если же вернулся <- err ->, выполнение load_config прерывается (по аналогии с тем, как --> прерывает цикл), возвращая ошибку вызывающей стороне.

Итог: единая алгебра вместо зоопарка типов

За счет объединения булевой логики, опциональных типов и результатов с ошибками в четыре формы < ... | ... >, Edva избавляется от дублирования концепций:

  • Вместо отдельных bool, Option и Result — единый параметризуемый тип { i1, i64 }.

  • Вместо if/else, match, .unwrap_or() и if let — единый оператор выбора | с позиционными ветками.

  • Вместо ручных флагов выхода из циклов — оператор >--, связывающий управление циклом напрямую с системой типов.

Ссылки

ссылка на оригинал статьи https://habr.com/ru/articles/1104020/