В релизе 3.1.0 появилась интересная фича: проверяемые исключения. Да-да, как в java, но немного по-другому.

Во-первых, проверка не обязательная, а специально подключаемая в импортах. Оборачивать все в try-catch чисто ради удовлетворения компилятора не обязательно. Во-вторых, команда разработчиков попыталась хотя бы частично решить проблемы java-подхода.

Одна из основных проблем с непроверяемыми исключениями в том, что… их не проверяет компилятор, они вне системы типов. Поэтому некоторые упарываются и отказываются от исключений совсем, заворачивая все в монады. Это генерирует много новых проблем, например, вопрос композиции этих монад (куча паттернов типа Final Tagless или Free Monad и супермонады типа ZIO — это все варианты решения). Но код с исключениями легко писать и читать, да и “happy path” не несет накладных расходов.

В новой версии компилятор проверяет, что если в теле функции есть throw, то исключение прописано в сигнатуре. Внутри сахара — контекстные параметры, которые будут удалены после компиляции. Это как неявные параметры (implicit val) в scala 2. Т.е. если функция может кидать исключения, то она требует неявный параметр, который свидетельствует о том, что оно будет обработано. А генерится этот параметр уже в блоке try-catch.

Увы, не все так идеально, как хотелось бы: есть проблема с объявлением по-настоящему чистых функций и с оборачиванием в try-catch лямбд. Но выглядит все довольно перспективно, на мой взгляд. Если эффекты можно будет выразить через исключения, то читать код будет легче.

UPD: Видос от Одерски по теме.