Я портировал приложение с Go на Scala, и это было довольно интересно.
Когда-то я играл с Haskell, который похож на Hardcore Mode для многих программистов, обученных языкам C-семейства, таким как Java, PHP и Javascript. Я проработал Напиши себе схему за 48 часов и настроил Atom с пакетами Haskell специально для этого случая, что помогло мне понять, как два совершенно разных стиля пришли к одному и тому же результату. В конце концов я разобрался со Stack, который казался гораздо более удобным для пользователя, чем песочница Cabal, и написал простого бота Telegram, который просто отвечал случайными картинками с котиками из Giphy. Большая часть этого работала довольно хорошо, за исключением одной путаницы с монадами (как и следовало ожидать).
Ближе всего к функциональности, которую я получил с реальным производственным кодом, были реактивные расширения Microsoft, которые теперь реализованы на многих языках. Это казалось действительно элегантным для программирования с графическим интерфейсом (и веб-программирования при работе с одностраничными приложениями), но также требовало обучения. Я думаю, что кривая обучения Rx была для меня круче, чем у Haskell, потому что каждая библиотека должна была реализовать функциональное реактивное программирование на своем основном языке, а они не всегда подходят для этого. Это похоже на GPL — любой код, который затрагивает компонент FRP, скорее всего, должен быть переписан, чтобы следовать той же самой парадигме, иначе ваш код будет завален преобразованиями в различные виды субъектов и наблюдаемых и обратно.
Я никогда не писал ни строчки на Scala до прошлой недели, когда решил переписать микросервис, который впервые был собран на Go. Go хорош во многих отношениях, потому что он пытается сделать вещи максимально простыми и понятными за счет того, что вам приходится выполнять большую часть ручной работы самостоятельно. Параллелизм в Go довольно элегантен, и как только вы адаптируетесь к его ментальной модели, вы сможете без особой суеты объединять основные вещи. На самом деле, единственное, что я искал, когда писал этот код на Scala, — это эквивалент каналов Go.
Scala ни в коем случае не совершенен, потому что ни один язык не совершенен. Однако я не получил большого удовольствия от поиска эквивалента сопрограммы или реализации многопоточности, которая работает на уровне языка. В одном комментарии, который я нашел, говорилось, что создание процесса раньше было нормальным, но теперь оно устарело. Некоторые говорят, что используют фьючерсы, которые отлично подходят для разовых задач, но не для чего-то долгоживущего и повторяющегося. Многие другие предлагают добавить Akka в качестве зависимости и перейти на модель в стиле Erlang. Параллелизм — это не просто, я понимаю, но это звучит как-то слишком! Возможно, я искал неправильные термины.
Тем не менее, я был в восторге от выразительности, которую предоставляет Scala, которая заставляет меня чувствовать, что я все еще могу писать код в стиле Ruby, а также получить приличную систему типов бесплатно. Это далеко не уровень Haskell по статической проверке всех функций только в системе типов, но у него хороший баланс, если вы проявляете осторожность и не увлекаетесь той мощью, которая у вас есть. Одна вещь, которая мне нравилась, это построение базовой таблицы истинности:
Это, вероятно, не идеально, потому что я написал Scala всего около трех дней, но мне очень нравится, как это выглядит по сравнению с кучей вложенных выражений if
и else if
. С ними вам нужно отслеживать различные комбинации на разных уровнях кода, а с Go вам потребуется множество err !=nil
проверок по пути.
При этом функциональный стиль предлагает некоторую серьезную элегантность и прямоту, потому что он предлагает взять на себя ответственность за большую часть связующего кода, который вы должны написать, чтобы он работал в императивном стиле или стиле ООП. И в этот момент все сводится к вопросу о неявном и явном. Я думаю, что компилятор должен быть как можно более интеллектуальным, чтобы ваш код касался исключительно вашей бизнес-логики или варианта использования, а не заставлял компилятор его понимать. Чем больше компилятор делает, тем лучше, потому что компилятор — это самая эпичная форма DRY: если вы можете статически проверить его один раз, вы можете статически проверить его для всех.
Именно поэтому я так сильно заинтересован в Rust, у которого есть такая же отличная перспектива создания функциональных абстракций с нулевой стоимостью, чтобы обеспечить очень выразительную альтернативу C++. Преимуществами функционального программирования можно пользоваться далеко за пределами функционального языка. Единственное, что останавливает меня от написания Rust, это то, что, к сожалению, его кривая обучения выше, чем у Scala.