Firefly — это новый язык программирования общего назначения, который пытается одновременно достичь удобства и безопасности за счет повсеместного внедрения зависимостей. Нет глобального доступа к файловой системе, сети, другим процессам или устройствам. Вместо этого вы получаете к ним доступ через системный объект, который передается основной функции, которая, в свою очередь, может передавать этот объект другим методам. Идея состоит в том, чтобы дать программисту детальный контроль над тем, какие части кода могут получить доступ к чему (к Log4Shell кто-нибудь?), не вводя монады или другое явное отслеживание эффектов.

Как оказалось, этот механизм позволяет использовать бесцветный async/await.

Поскольку доступ ко всему вводу-выводу осуществляется через внедрение зависимостей, вызов функции верхнего уровня может быть асинхронным только в том случае, если он вызывается с асинхронным аргументом. Это приводит к первому правилу вывода async/await в Firefly:

Правило 1. Вызовы функций верхнего уровня могут быть асинхронными, только если они вызываются с асинхронными аргументами. Каждая функция верхнего уровня получает синхронную и асинхронную версии. Если один из аргументов асинхронный, вызывается асинхронная версия; в противном случае вызывается версия синхронизации.

Внутренние функции также могут захватывать асинхронные объекты и вызывать их. Однако это может происходить прямо или косвенно только из аргументов функции верхнего уровня. Это приводит ко второму правилу.

Правило 2. Внутренние функции являются асинхронными, если они вызывают асинхронную функцию. Такие функции всегда прямо или косвенно исходят из аргументов метода верхнего уровня.

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

Правило 3. Все аргументы и возвращаемые значения потенциально являются асинхронными, если хотя бы один из аргументов является асинхронным. Возвращаемое значение может впоследствии привести к тому, что аргументы станут асинхронными.

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

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

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

Во второй части мы выводим async/await на конкретном примере кода и видим, как это работает внутри:

Читать часть 2: https://www.ahnfelt.net/async-await-inference-in-firefly-part-2/