Ссылки Сайт проекта Windows демо Главная фундаментальная особенность Аргентума - это его ссылочная модель данных, обеспечивающая не только безопасность памяти, как в Java-Rust-Go, но и полностью предотвращающая утечки. Но кроме этой особенности в Аргентуме есть еще много интересного - это сверхбыстрые динамические приведения типов и вызовы методов интерфейсов, модульность, "микросервисная" многопоточность, и управляющие конструкции, основанные на optional типе. О последней особенности и будет этот пост. Рассмотрим пример: На самом деле выражение a ? b : c. в этом примере - это два вложенных друг в друга бинарных оператора. И их можно записать и по отдельности:   Тип переменной x будет optional<int> или в синтаксисе Аргентума: ?int.  Остаовимся на семантике этих двух строчек подробнее: Можно сказать, что оператор `?` производит optional, а `:` потребляет его.Если они применены вместе, они работают как старый добрый тернарный оператор: Если результат выражения не нужен, оператор ? работает как if   А пара операторов ?: работает как if...else: Итак Аргентум разделил тернарный оператор на два бинарных. Что это дало?   Поддержаны короткие условные выражения без части else, которые возвращают значения. Язык стал ортогональнее. Ничего не усложнено - не добавлены никакие новые синтаксические конструкции. Появилось два конструктора optional-значений: вместо (C++) optional<decltype(x)> maybeX{x}можно написать maybeX = true ? x вместо optional<decltype(x)> maybeX{nullopt}можно написать maybeX = false ? xили maybeX = ?x Распаковка с использованием дефолтного значения тоже стала проще: вместо auto a = maybeX ? *maybeX : 42можно написать a = maybeX : 42 При отсутствии данных не обязательно использовать дефолтное значение, можно вызвать хендлер проблемной ситуации или удариться в панику: x : terminate() Очень часто возвращая optional результат функции мы пишемreturn condition ? result : nulloptв Аргентуме это будет так: condition ? result Мы можем не только комбинировать операторы ? и :. Мы можем комбинировать операторы : между собой.Например: user = currentUser : getUserFromProfile() : getDefaultUser() : panic("no user");Это короткое выражение попытается получить user-объект из нескольких мест, и вызовет выход из приложения если ничего не получится. Аргентум поддерживает упрощенный синтаксис для создания optional-типов: Вместо true ? x можно написать +x Вместо false ? x можно написать ?x или ?int В Аргентуме есть оператор {A; B; C}, который исполняет A B C по очереди и возвращает результат C. Обратите внимание на отсутствие точки с запятой ";" после после C. Если она там будет, это будет означать, что в конце всего блока {} есть еще один пустой оператор, и его результат (void) станет результатом всего блока {}.Блок - может группировать несколько операторов: Блок позволяет создавать локальные переменные:  Блок может присутствовать в других выражениях, например быть инициализатором переменной. (Еще блок может быть таргетом для оператора break/return, но это тема отдельного поста). Совместное использование блоков и оператора "?" позволяет создавать условные выражения, аналогичные оператору if..else, которого в Аргентуме, кстати, тоже нет: Что такое значение типа optional<T>? Это или сам T или особое значение nothing, не совпадающие ни с каким значением T. Теперь попробуем новый трюк: что такое optional<void>? Это однобитное значение, позволяющее различить nothing и void. Это полный синоним логического типа данных. Получается, что если в нашем языке есть тип void и есть и optional<T>, то отдельный тип bool нам уже не нужен. Все логические операции, например операции сравнения, в Аргенуме возвращают optional<T>. А все операции принимающие логические значения теперь будут принимать любые разновидности optional<T>. Что это нам дает? Оператор ? на самом деле имеет тип: (?T) ? (T->X)  -> (?X)   Cлева не только bool (который на самом деле ?void) но и вообще любой ?T Cправа - выражение с результатом X (как вариант, превращающее T в X) Результат самого оператора ? будет ?X.  Как работает оператор "?" Вычисляется/исполняется левый операнд и анализируется его результат: Если "nothing", результатом всего оператора ? становится "nothing" типа ?X. Иначе Создается специальная временная переменная  `_` типа T, которая получает значение извлеченное из ?T. Исполняется правый операнд, которое может использовать переменную "_". Результат правого операнда должен иметь тип X, он упаковывется в ?X и становится результатом всего оператора ?. Такая конструкция оператора "?" позовляет проверять условия с одновременным излечением завернутого в optional значением и передавать результаты дальше, объединяя в конвейер цепочки операций, вызовов методов, доступов к полям в безопасной контролируемой манере: В этом примере: Если currentOrderId существует, найти по нему order. Если order нашелся, взять из него price. Если price есть, обработать его. Если в языке отсутствует такой синтаксис, это простое выражение превращается в многострочную трудно сопровождаемую простыню `if`-ов и временных переменных. Кстати о безопасности на уровне синтаксиса, и C++ и Java позволяют обратиться к значению внутри optional без проверки на его существование: А в Аргентуме оператор "?" откроет вам доступ к внутреннему значению только при его наличии,  а оператор ":" потребует указать код, который предоставит значение вместо отсутствующего. Других операторов для доступа к optional нет. Это непробиваемая защита от обращений к несуществующим значениям на уровне синтаксиса языка на этапе компиляции.   Кстати, в Аргентуме есть ключевые слова bool, true, false. Они декларируют тип ?void, и создают значения +void и ?void, чтобы все было просто и привычно. В последнее время стало модно добавлять в разные языки Null safety.  В Аргентуме Null safety обеспечивается не добавлением новых понятий, а убиранием ненужных. Указатели в Аргентуме не бывают nullable. Если нужен nullable-указатель, используется optional-обертка над указателем, где optional nothing - это аналог null: Кстати, синтаксис ?T для создания пустых указателей означает, что в Аргентуме все "null pointers" строго типизированные. В Аргентуме тип optional глубоко встроен в язык. Для разных обертываемых типов его внутреннее  представление различается. Например, optional-указатели на самом деле хранятся как простые указатели, и optional-nothing кодируется в них как 0. Это обеспечивает бесплатный маршалинг в други языки через FFI, компактность внутреннего представления и высокую скорость работы. ТипыObject и ?Object с точки зрения языка различаются только на стадии компиляции - для первого не нужны проверки на null, для второго наоборот запрещается обращение без проверки. Аналогичный прием используется для ?double, в котором optional nothing - это просто NaN. Еще одна модная тенденция обвешивать условный оператор какими-нибудь вычислениями в условии, результат которых сохраняется в локальной переменной доступной в ветках условий, например в C++: Чтобы это работало, достаточно, чтобы v приводился к bool. Однако очень быстро выяснилось, что приводить один и тот же тип к логическому типу можно по разному. Например для строки иногда полезно считать как бы false пустую строку, иногда строку с текстовым значением "false", иногда к этому можно добавить строку "0" или еще как-то. Поэтому в C++17 появился вот такой удобный вариант if Как это работает? Вначале вычисляется expression его результат помещается в локальную переменную v, потом она передается в predicate который на ее основе делает bool, которые выбирает нужную ветку в которой эта v доступна. Например: Вот как это делается в Аргентуме без никакого дополнительного синтаксиса: Где: expression по-прежнему отдает значение T, predicate анализирует его и превращает в ?T со значением внутри, a оператор ? при удачном решении предиката передает распакованное из `optional` значение T в use. Пример: Таким образом Аргентум реализует эту возможность, без введения в язык новых сущностей.   В коротких выражениях имя "_" удобочитаемо и уместно, однако больших конструкциях, оно может становиться проблемой и конфликтовать, особенно если несколько операторов "?" вложены друг в друга. Поэтому существует синтаксическая разновидность оператора "?", которая позволяет давать переменной явное имя. Рассмотрим менее абстрактный  например: Эта функция проверяет, есть ли у текста последний абзац и есть ли у документа стиль с указанным именем, прежде чем прикладывать стиль к абзацу. И это все делается без введения переменных уровня блока, засоряющих пространство имен и продлевающих жизнь объекту сверх необходимого. Кроме того эти переменные связываются не с optional-значением, а уже с распакованным, прошедшим проверку на nothing-ness. Оператор expression ~ ClassOrInterface выполняет два вида приведения типов - если класс является базовым для выражения, то результат такого приведения гарантирован, и операция имеет тип ClassOrInterface во всех остальных случаях операция выполняет быструю рантайм проверку типа, и результат операции будет optional:?ClassOrInterface. В отличие от кастов в Java и C++ где проверка результата каста является необязательной (и очень многословной), в Аргентуме синтаксически невозможно обратиться к зачению завернутому в optional-тип, и поэтому приложение на Аргентуме просто не может упасть из-за ошибочных типов: В аргентуме ассоциативные ссылки (так же известные как weak pointers) - это один из трех базовых встроенных в язык типов указателей. Такие ссылки легко создаются, копируются передаются и хранятся, они nullable сами по себе: Процесс разыменования такой ссылки включает в себя проверку, что ссылка вообще на что-то ссылается, что ее таргет все еще существует и находится в том же потоке. Результат всех этих проверок - временная ссылка на объект, завернутая в optional, которая не только сигнализирует, о доступности объекта по ссылке, но и предотвращает его удаление. Вышеописанное разыменование не имеет никакого синтаксиса. Оно выполняется автоматически везде, где &T преобразуется в ?T. Например, в опреаторе "?":   Оператор ? хочет получить слева ?T, поэтому weak-pointer obj будет локнут, и при успехе передан в правый операнд в виде имени "_". В результате: в Аргентуме невозможно обратиться по weak-ссылке, которая потерялась эта проверка имеет супер легковесный синтаксис она порождает временное значение ("_" или имя определенное программистом через ?=name), и это временное имя имеет время жизни органиченное правым операндом  оператора?. Все стандартные контейнеры  в результате операции индексации x[i] возвращают ?T. Поэтому Аргентум делает невозможным обращение за пределы массива или по не валидному ключу. Кстати, операция индексации - это сахар для вызова метода getAt | setAt. Определяя такой метод (с произвольным количеством параметров произвольных типов) вы превращаете свой класс в многомерный контейнер. Optional-обертка может содержать в себе любой тип, включая optional. Теоретически это позволяет иметь типы ?int, ??int, ???int и т.д. Зачем это нужно? Например, при индексации контейнера с bool или optional- элементами нужно как-то различать отсутствие элемента (выход за границу массива) и элемент со значением nothing. Или при использовании вложенных ?-операторов. Результат будет зависеть от того, какое из условий не сработало. Рассмотрим пример: Предположим, что getUserToken возвращает значение ?String, которое будет или токеном, или nothing, если у этого user-a нет токена. Тогда правый самый внутренний оператор ? вернет ??String, который буден nothing, если нет user-a, just(nothing) если нет токена, и just(just(string)) если есть токен. Тогда левый оператор ? будет иметь тип ???String который буден nothing, если нет name, и все сорта just(...) сигнализирующие о проблемах с поиском user-a и его токена. Получившийся пакет из optional-значений можно проанализировать  тремя операторами ":" Но такая вложенность нужна не всегда. И поэтому в Аргентуме есть оператор &&, который работает почти как ?, но он требует, чтобы и правый левый операнды возвращали optional значения (не обязательно одного типа). Он работает совсем как maybe >>= Хаскеля. Его левый операнд возвращает ?T, а правый - преобразует T в ?X. Результат оператора - ?X. Вначале он исполняет левый операнд: если он nothing, результат становится nothing типа ?X. иначе он связывает внутренне значение из ?T с переменной "_" и исполняет правый операнд, результат которого и становится результатом всего оператора &&. По сравнению с оператором ? оператор && имеет всего одно отличие - он не упаковывает результат правого операнда в optional, вместо этого он требует, чтобы он сразу был optional. Если подставить bool (optional void) вместо ?T и ?X в оператор && он становится полностью идентичным оператору && во всех Си-подобных языках. Естественно, как и оператор ?, оператор && имеет форму &&=name, для задания имени вместо "_". Перепишем пример выше: Теперь token имеет тип ?String. В нем пропала вся информация о том, почему токен не удалось получить. Теперь это или "нет токена" или значение токена. Иногда вложенность optional-оберток полезна, иногда - нет, поэтому и оператор ? и оператор && найдут себе применение. Последний из не рассмотренных операторов - "||". Он похож на ":". Его единственное отличие - он требует, чтобы и справа и слева был один и тот же optional тип. Если оператор ":" возвращает свой левый операнд распакованным из optional-a, то "||" этого не делает. И в этом он также аналогичен оператору || изо всех Си-подобных языков. Примеры использования: Аргентум использует привычные операторы для условий и блоков, но благодаря использованию типа данных  optional вместо bool, он добавляет к ним новое измерение - они начинают не только передавать управление, но и данные. Благодаря этому синтаксис упрощается, выражения становятся более лаконичными и выразительными. Аргентум не вводит ни одной новой конструкции, наоборот, убираются дублирующие. Аргентум упрощает и инфорсит проверки на null, потерю weak, результатов приведения типов, индексов и ключей контейнеров с тем, чтобы эти проверки были естественной частью бизнес-логики, а не обработчиков исключений. Внутреннее представление optional-типов бесплатно для всех указателей, и большинства типов значений.