Мои впечатления от языка Scala после года работы с ним
19 августа 2015
Со Scala я начал играться еще в марте 2013-го года. Но тогда я писал на ней только hello world’ы на 100-200 строк кода. Писать настоящий, боевой, код на Scala я начал только год назад. Само собой разумеется, это совсем другой опыт по сравнению с написанием hello world’ов. В данной заметке я поделюсь своими впечатлениями от использования Scala, расскажу про сильные и слабые стороны языка, и так далее.
Что хорошо:
- Строгая статическая типизация с автоматическим выводом типов, как в Haskell или SML. Ощущения от программирования такие же, как от программирования на Haskell — если программа компилируется, в ней вряд ли есть ошибки. Особенно, если указать побольше флагов -Y, придающих языку дополнительную строгость. О пользе типов в этом блоге было сказано немало, см например раз и два, поэтому не будем подробно останавливаться на этом пункте;
- Несмотря на строгость, пишется на языке так же легко и приятно, как на скриптовых языках. Про Haskell я определенно не могу сказать того же;
- Очень приятный и лаконичный синтаксис. Это особенно хорошо заметно в книге «Learning Spark», где приводятся примеры кода на Java, Scala и Python. Код на Scala всегда получается той же длины, что и код на Python, и раза в два короче, чем код на Java. При этом код на Scala и Python намного более читаем;
- Функциональное программирование с неизменяемыми переменными, лямбдами, замыканиями, кортежами, списками и так далее. За счет повсеместного ФП код становится намного проще поддерживать, плюс существенно упрощается написание многопоточного кода;
- Раз заговорили о многопоточности, то тут есть акторы, которые оказались прямо-таки ничем не хуже акторов в Erlang. В отличие от подхода с мьютексами и атомарными переменными, писать многопоточный код на акторах — одно удовольствие. Гонки, дэдлоки и многие другие проблемы уходят сами собой. Также акторы дают программисту множество удобных готовых примитивов — наблюдение за другими акторами, шедулеры, сброс состояния в случае возникновения непредвиденных ошибок (let it crash). Без акторов все это очень быстро придется изобретать самостоятельно;
- Судя по синтетическим и ничего на практике не означающим бенчмаркам, язык числодробит не хуже, чем Java. Это действительно здорово, когда твой код получается достаточно быстрым с первого раза. Не нужно постоянно искать узкие места и подпирать их написанием половины проекта на основном языке, а половины — на Си;
- За счет паразитизма на JVM язык имеет не только мощный GC, JIT-компилятор и кроссплатформенность, но и великое множество готовых библиотек практически на все случаи жизни, а также кучу крутых инструментов разработки, например, YourKit;
- Широкая область применения. Компилятор Scala написан на самой Scala. Достоверно известно об успешном применении Scala не только при написании веба и бэкендов, но и десктоп-приложений, а также мобильных приложений под Android. Вероятно, можно писать и под iOS, используя RoboVM, но о реальных случаях использовании Scala для решения этой задачи мне не известно. Еще, говорят, можно писать фронтэнды на Scala.js. Мне даже рассказывали о создании при помощи Scala.js кроссплатформенных мобильных приложений по принципу SPA. Но сам я пока Scala.js не пробовал, так что не могу сказать, насколько хорошо или плохо получается;
- Язык не ограничивает выбор программиста небольшим набором «правильных» средств, которые предположительно должны одинаково хорошо решать все задачи. Если задача хорошо ложится на акторы, используем акторы. Но также можно использовать мьютексы, атомарные переменные, агенты, volatile переменные, параллельные коллекции или, например, STM. После Erlang, в котором без плясок с бубнами даже обычный LRU-кэш написать нельзя, это просто прекрасно. Аналогично, там, где это удобно, пишем в функциональном стиле, иначе пишем в императивном. Если же вдруг задача хорошо ложится на ООП, то можно воспользоваться и ООП. А еще на всякий случай есть рефлексия, макросы, аспекты и другие средства. Считаю, что это примерно как cherry-pick в Git, или возможность менять размер шрифта в терминале. Большинству людей никогда не понадобится, но иногда может сэкономить целые дни работы и кучу нервов;
- Слухи о медленной компиляции и тормознутости IDE оказались сильно преувеличены. В частности, на Scala можно без проблем писать реальный, большой, проект на слабеньком ултрабуке, купленном 2.5 года назад, я проверял. Сборка проекта с нуля занимает 2 минуты. За счет инкрементальной сборки обычно компиляция происходит за пару секунд. Если же проект очень крупный, его всегда можно разбить на артефакты, ну или микросервисы. На практике куда более серьезной проблемой, чем время компиляции, является долгий прогон тестов. Следует также учитывать, что, как отмечалось выше, 1000 строк кода на Scala соответствует примерно 2000 строкам кода на Java, что нужно принимать во внимание при сравнении скорости компиляции;
- Опять таки, вопреки слухам, язык совсем не сложный. Я бы сказал, в отношении Scala путают сложность языка и большое пространство возможных решений конкретной задачи на этом языке, потому что, как было отмечено выше, Scala не говорит вам «используте только акторы и ФП». Это хорошо, потому что множество удачных решений в узком пространстве поиска является подмножеством удачных решений в более широком пространстве. То есть, в более широком пространстве можно найти такое хорошее решение, какого совсем нет в более узком пространстве. Единственная проблема заключается в том, что на выработку хороших эвристик для поиска в широком пространстве требуется немного больше времени. Но это никак не относится к сложности языка. Scala — простой язык, не сложнее, скажем, Python. При этом кривая обучения у Scala довольно пологая, чего не скажешь, например, о том же Haskell;
- Зрелость языка. Отличные инструменты разработки, не исключая полноценной IDE, куча готовых библиотек как из мира Java, так и собственных, множество саксес сторис, большое сообщество программистов, книги и документация, широкий выбор вакансий — все в наличии;
Есть, конечно же, некоторые неприятные моменты. Сразу отмечу, что на фоне всего названного выше это скорее мелкие придирки, чем серьезные проблемы. Но без них картина не была бы полной. Итак, что плохо:
- На границах взаимодействия с Java-кодом может возникать NPE. Приходится писать небольшие пакеты-обертки с Option и try-catch. Это небольшая проблема, так как все NPE отлавливаются еще во время разработки и тестирования. Но лучше бы их, конечно, совсем не было;
- Если вы раньше никогда не работали с футурам и Akka, то можете очень легко наплодить гонок, сделать блокирующий вызов на общем тредпуле или забить тредпул футурами по одному запросу, из-за чего остальные запросы начнут тормозить. Это тоже небольшая проблема, так как она полностью устраняется повсеместным использованием for comprehensions, созданием отдельных пулов для блокирующих вызовов и тяжелых вычислений, оборачиванием обработчиков сообщений у акторов в небольшие вспомогательные методы, использующие become и stash, использованием «медленного» traverse, и так далее. Но если заранее не знать про все эти проблемы, можно собрать гору граблей. Лучше бы этих проблем, опять таки, совсем не было, как, например, их нет в Go или Erlang;
- Все-таки, было бы круто иметь скорость компиляции, как в Go, а также IDE, которая вообще никогда даже немного не подтормаживает, в том числе при крупных рефакторингах и редактировании файла на 5000 строк кода;
- Не столько проблема языка, сколько человеческий фактор. Typesafe иногда делает странные вещи, вроде этого их Activator, про который никто не понимает, зачем он нужен. Или постоянно призывает всех быть реактивными и использовать для всего Akka Streams. Увы, не все программисты способны отделять полезную информацию от маркетингового булшита, из-за чего общаться с ними становится сложно. Вообще, сообщество Scala-программистов, по моим наблюдениям, довольно своеобразное. ФП головного мозга у одних программистов, и ООП головного мозга у других. Одни пытаются для всего использовать Scalaz с монадами и функторами, вторые пишут развесистые иерархии классов и фабрики фабрик. Представления о том, как работать с акторами, у некоторых скалолазов могут сильно отличаться от представлений, скажем, почти всех программистов на Erlang, откуда эти акторы, собственно, и пришли. Конечно, есть и нормальные ребята, которые вместо споров о религии думают, как писать простой и понятный код, решающий конкретные задачи. Но, в общем и целом, не все Scala-программисты одинаково полезны;
- К сожалению, строгая типизация не всегда такая уж прям строгая. Например, в Scala можно сравнивать любые два объекта. Обязательно прикрутите в вашем проекте как можно больше флагов scalac, увеличивающих строгость языка. В том числе, всегда используйте флаг
-Xfatal-warnings
; - Куча всяких мелочей. Со scalable синтаксисом не все и не всегда гладко. Скажем, можно забыть написать new. Код тайпчекнится и скомпилируется, потому что в результате будет вызван метод apply у companion object. Но в рантайме программа упадет с очень странным исключением. Еще в Scala есть метод без скобочек
def x
, есть метод со скобочкамиdef y()
и есть метод принимающий Unitdef z(t: Unit)
, все это немного разные вещи. Также есть structural typing и атомы, как в Erlang или Lisp, но на практике их почти никто и ни для чего не использует. По умолчанию все методы public (почему так сделано). Deb-пакеты получаются большими, 50-70 Мб считается здесь абсолютно нормальным размером. Ну и другие такого рода вещи;
Думаю, что нет такого языка, который нравился бы абсолютно всем. Каждый программист имеет собственную уникальную функцию от многих аргументов, определяющую fitness языка для разных классов задач. Для кого-то все решает красота и декларативность языка. Есть и такие, для кого решающим фактором является наличие remsh. Что до меня, то моя функция включает в себя такие параметры, как (!) возможность быстро и качественно решать задачи, которыми обычно я занимаюсь, скорость выполнения программ, наличие строгой статической типизации, наличие библиотек и инструментов разработки, возможность нанять где-то программистов на этом языке и самому устроиться куда-нибудь программистом на нем. А также другие. На данный момент для Scala значение моей функции fitness для решаемого мной класса задач имеет наибольшее значение по сравнению со всеми другими языками, которые я пока что видел. Более того, сейчас я с трудом представляю язык, значение функции fitness для которого было бы существенно выше.
Если хотите знать мое мнение, вот оно. Scala далеко не идеальна. Но это нормальный язык, как для написания веба и бэкендов, так, по всей видимости, и множества других задач. Если вы начинаете новый проект или не вполне довольны языком, используемым вами в настоящее время, присмотритесь к Scala повнимательнее.
Метки: Scala, Функциональное программирование.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.