Комментарии 126
Самое интересное начинается, когда знакомишься с закидонами отдельных языков, когда начинаешь видеть прыщи на напудренном личике компиляторов. Когда на Си начинаешь использовать callback, на плюсах — колдовать compile-time шаблоны, в шарпах синхронизировать потоки, изменять java-класс на лету не перезапуская приложения, оборачивешь в haskell'е подтекающий поток и пытаешься разобраться, какой из случайно подключенных файлов-библиотек заменяет твой метод в Ruby.
То что вы описываете — это уже тонкости. Далеко не каждый с ними вообще столкнется. Можно спокойно программировать много лет, ничего про это не зная. Вы не будете экспертом — но вы не будете при этом и начинающим.
Тогда это называется изучением компилятора (или платформы), но не изучением языка. Язык — штука идеализированная. И понять чего она стоит действительно не сложно. Иногда мне кажется, что можно ввести чеклист на две сотни опций, заполнив который, можно выяснить всё что нужно о функциях языка.
И это всё часть языка, потому что нельзя использовать язык отдельно от балласта. И во многом язык определяет всё остальное. Например, консервативный и строгий Си великолепно и быстро транслируется в ассемблер именно из-за его низкоуровневости. А изначальный математический подход хаскеля к данным как к неизменному множеству определил как необходимость монад, так и яркий сексуальный опыт выхода в многопоточность. Думаю, и Питон тоже сможет удивить постоянным неуместным копированием данных.
Если ограничиться только синтаксисом и концепциями верхнего уровня, можно получить неверное общее мнение о языке. Одно время хабровчане фанатели от рубинов, сейчас эта любовь плавно перетекла к Go и Scala. Хотя и тот, и другой имеют очень много изъянов, прежде всего по ту сторону монитора. Последний, к примеру, не очень расторопен при компиляции, что для привыкшего к тестам дебагом программиста не очень приятно.
Где вы откопали это утверждение? Авторы Scala разрабатывали новые языки (Pizza, GJ) задолго до появления c#, скажем так, примерно лет за пять. И в истории появления языка C# кажется не упоминается вовсе. Можно цитатку?
При этом заметьте, что сегодняшний и тогдашний c# — это две большие разницы. В тогдашнем не было почти ничего такого, что могло бы вдохновить на создание нового языка на платформе JVM, тем более людей, которые в 2001 уже создали компилятор Pizza, где уже были явные намеки на ФП (функции как сущности первого класса, например).
А проект этого языка, как и GJ (прототип generics) и подавно появились где-то в 1998, когда c# просто не было. Не мог он вдохновить никого, просто по времени не выходит.
Насколько я понимаю, он тогда уж на F# похож.
В Scala довольно сильно похожа на OCaml. Конечно, отличий в синтаксисе довольно много, но идеологическое наследие довольно легко проследить. К примеру, систему модулей из ML в Scala смогли унифицировать с объектной системой: вместо module type теперь обычный интерфейс (trait), вместо module — object, вместо функторов — наследование.
Хм, даже нашлась довольно интересная презентация на эту тему: http://lambdafoo.com/scala-syd-2015-modules/
F# так вообще по сути OCaml на .NET и без функторов.
3 года на уровне читателя что-то и правда многовато.
Переход в другие парадигмы «на 5 минуток» точно так же не даёт эффекта. Да, я выучил, с чем едят монады и как делать функторы. Но я ещё не распробовал их вкус, на каких задачах я буду с удовольствием разжёвывать лямбды, а на каких я опорожню желудок и потребую обратно мои богомерзкие плюсы, потому что не могу контролировать накладные расходы этапа выполнения.
Так как Scala-разработчиков сложно искать, у нас начали брать Java-разработчиков и переучивать. Пока это проблем не вызывало. Scala они осваивают очень быстро, сложность языка преувеличена.
сложность языка преувеличена
Спорное утверждение. Сильно зависит от того, как на нём писать. Если на нём пишут недавно переученные Java-программисты, то, наверное, код принципиально не сильно отличается от аналогичного Java-кода и потому выглядит простым.
На мой взгляд, это один из самых сложных языков. Он мне показался сложным даже после повторного прочтения Programming Scala во времена Scala 2.8, и с тех пор ещё много всего добавилось (макросы, к примеру). Система типов слишком тяжеловесная (generic classes, abstract types, existential types, compound types, path-dependent types, dear god please make it stop).
Помню, пытался реализовать свой Option[T]
для разминки. Казалось бы, должно быть просто, но в итоге убил кучу времени и всё равно получал ошибки компиляции, которые было сложно расшифровать. К примеру, написать аналогичный Maybe a
в Haskell — дело десяти минут, даже если не использовал язык долгое время. Cake-pattern тоже тяжело даётся, даже после опыта с compile-time dependency injection через функторы в OCaml.
Это отсылка к слайду в презентации Роба Пайка "Another Go at Language Design"
public static <I, O> ListenableFuture<O> chain(ListenableFuture<I> input, Function<? super I, ? extends ListenableFuture<? extends O>> function) dear god make it stop
a recently observed chat status
http://web.stanford.edu/class/ee380/Abstracts/100428-pike-stanford.pdf
Написать хорошую библиотеку, которой можно легко пользоваться, действительно сложно. Но, по большому счету, без этого обычно можно обойтись. Уже реализовано достаточно много доступных сторонних хороших библиотек (тот же Slick), которые внутри устроены сложно, но пользоваться ими легко.
А тот код, который требуется для «энтерпрайза», пишется легко и обязвтельного глубокого знания сложных особенноетей Scala не требует.
Написать хорошую библиотеку, которой можно легко пользоваться, действительно сложно
Написать хорошую библиотеку в принципе сложно, независимо от языка. Моё утверждение в другом: даже простые задачи в Scala существенно труднее, чем они должны быть, из-за сложности языка. Понятно, что энтерпрайз в основном заключается в перекладывания данных из одного XML в другой и валидаций, и бОльшая часть кода относительно простая, но когда требуется сделать что-то нетривиальное, я обычно думаю "как бы я абстрагировал это в Haskell?", получаю тривиальное решение, а потом пытаюсь смоделировать его в Scala.
Вот все говорят про "тайп-классы на имплиситах", а мне вот тайп-классы нравятся гораздо больше. Синтаксически гораздо легче, и не нужно помнить замысловатые правила поиска неявных объектов в разных скопах.
В OCaml, кстати, тоже имплиситы завезти хотят Modular Implicits
Не нужны тайп классы и имплиситы для промышленной разработки. Разве что для тестов — чтобы красивый DSL сделать.
имплиситы для промышленной разработки
Да ладно, каждый первый первым делом объявляет всякие коннекторы к базе и тред-пулы имплиситами.
При рефакторинге возникают некоторые проблемы, но не очень большие, если есть опыт их решения.
для протаскивания существует dependency injection. Скаловский подход с cake pattern, синглтонами и имплиситами ошибочен.
А также
5) много дополнительного кода
6) сложно конфигурировать в рантайме, т.к все зависимости прописаны в коде
7) самое важное — сложно тестировать, т.к. запуск модуля с парой моков вместо реальных классов сделать очень непросто. Также подход с синглтонами усложняет параллельное тестирование
По моему опыту, самый оптимальный DI для больших проектов на скале — это Guice.
Вообще печально наблюдать, как академическое scala-сообщество наступает на уже отполированные Java/C++ программистами грабли. Что синглтоны — это плохо, что перегрузка операторов превращает программу в нечитабельное мессиво...
Проблемы хорошо расписаны тут, например: http://www.warski.org/blog/2011/04/di-in-scala-cake-pattern-pros-cons/
Вот потом этот каждый первый и идет в собственный бложик крыть скалу разными частями тела. За ее сложность.
Ну вот видите, на мой комментарий ответили два разработчика, один считает подход очень удачным, другой — очень неудачным. Теперь найдите Haskell-программиста, который считает typeclasses плохой идеей.
Так хаскель, я надеюсь, не претендует на роль промышленного языка? Или на простоту?
Или на простоту?
А почему нет? Хаскель — это довольно простой и логичный язык. Я считаю его языком на порядок более простым, чем OCaml и, тем более, Scala. Там не нужно помнить множество правил, синтаксических конструкций, компот из разных систем типизаций и неявных объявлений, чтобы понимать, как компилятор будет интерпретировать твою программу.
Вся "сложность" Haskell в том, что думать по-другому и разбираться в абстракциях, чтобы использовать язык и библиотеки эффективно.
на роль промышленного языка?
Пожалуй, основная деталь, которая мешает Хаскеллу в проде — это вездесущая ленивость по-умолчанию. Она существенно затрудняет анализ производительности и при неумелом использовании ведёт к так называемым space leaks, которые могут выстрелить в самый неподходящий момент.
Эта одна из причин, по которой разработчики из Standard Chartered (которые владеют более чем миллионом строк кода на хаскеле), написали собственный компилятор Mu, который по-умолчанию использует энергичные вычисления.
Если быть точным, это компилятор энергичного диалекта Haskell https://www.youtube.com/watch?v=hgOzYZDrXL0.
какая-нибудь инфа кроме этого видео?
Честно, говоря, про него мало что известно, исходников нет. Я сам узнал о существовании Mu относительно недавно из презентации Дона Стюарта на Google Tech Talk Haskell in the Large.
Пару деталей можно найти в блоге Дона:
Yes, we use Mu, which is our Haskell compiler. It’s source compatible with GHC, but with strictness on by default.
Кстати, в GHC начиная с версии 8.0 есть прагма Strict с аналогичным эффектом.
Имплиситы используются далеко не только для реализации классов типов. И сложности возникают именно с другими вариантами применения.
Сам я имплиситов немного побаиваюсь, но их использование в библиотеках часто делает использование этих библиотек гораздо удобнее.
Конечно, зависит от задачи. Очень многое на Haskell делается красиво и тривиально. Но когда начинается ввод/вывод и обработка ошибок Haskell почти ни чего не дает, по сравнению с распространенными языками.
Но когда начинается ввод/вывод и обработка ошибок Haskell почти ни чего не дает
Вот тут я не совсем понял. Что именно "не даёт" Haskell? Чего-то не хватает в стандартной библиотеке?
Библиотек для эффективного потокового ввода-вывода больше, чем должно быть, потому что приходится выбирать, какую из них использовать (извечный вопрос "Pipes или Conduits?").
Для обработки ошибок есть Either, который весьма удобно использовать вместе с MonadError. Исключения тоже есть, но они считаются bad practice и не такие удобные, как в той же java.
возможность сбоев надо сразу учесть, поскольку добавить ее в готовый код часто удается только основательно его переработав.
Ох, опять. Чтобы добавить корректную обработку ошибок в "готовый" код (т.е., видимо, в код, в котором её нет), нужно всё равно всё переписать, независимо от языка. Исключения не являются универсальным решением проблемы, скорее создают только больше проблем.
Не дает заметно большего удобства в программировании
Снижается роль ленивости и чистых функций
Вообще говоря, это очень спорные утверждения. Роль ленивый вычислений в обработке ошибок принципиальна. Именно благодаря ленивым вычислениям можно писать красивый монадический код с обработкой ошибок, в котором в каждой строчке не стоит if err != nil.
Смеха ради набросал небольшой примерчик, чтобы продемонстрировать идею и не быть голословным. Иногда всё работает успешно, в 10% случаев возвращается ошибка с описанием. Расширения языка используются в основном для небольшого сокращения бойлерплейта.
{-# LANGUAGE FlexibleContexts, ExistentialQuantification, Rank2Types #-}
import Control.Monad.Except
import System.Random (randomRIO)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import System.IO (hPutStrLn, stderr)
type Person = String
data FbError
= NetworkError String
| ApiError String
| InvalidAccount Person
deriving (Eq, Show)
type FbDownloader = ExceptT FbError IO
type FbFun a b = forall m . (MonadError FbError m, MonadIO m) => a -> m b
fbDb :: [(Person, [Person])]
fbDb = [ ("alice", ["bob"])
, ("bob", ["alice", "eve"])
, ("eve", ["bob"])
]
getFriendsOf :: FbFun Person [Person]
getFriendsOf person = do
x <- liftIO $ randomRIO (1 :: Int, 100)
case x of
_ | x < 6 -> throwError (NetworkError $ "Failed to download friends of " ++ person)
_ | x < 11 -> throwError (ApiError "Your api key is invalid :[")
_ | otherwise -> do
liftIO $ hPutStrLn stderr ("Requesting friends of " ++ person ++ " ..." )
case lookup person fbDb of
Nothing -> throwError $ InvalidAccount person
Just ps -> return ps
getFriendsOfAll :: FbFun [Person] [Person]
getFriendsOfAll = fmap concat . mapM getFriendsOf
getFriendsOfFriends :: FbFun [Person] [Person]
getFriendsOfFriends people = getFriendsOfAll people >>= getFriendsOfAll
runDownloader :: FbDownloader a -> IO (Either FbError a)
runDownloader = runExceptT
main :: IO ()
main = do
names <- getArgs
result <- runDownloader $ getFriendsOfFriends names
case result of
Left e -> hPutStrLn stderr (show e) >> exitFailure
Right l -> mapM_ putStrLn l
Да, Scala позволяет писать сложный обобщенный код, который будет потом переиспользоваться в самых неожиданных местах. Писать такой код сложно на любом языке. Но, в отличие от многих других языков, Scala позволяет сделать так, что пользоваться этим кодом можно не вникая в тонкости обобщенного программирования. То есть на Scala просто не только писать что-то простое, но и использовать написанное кем-то сложное. Это очень сильно снижает уровень вхождения, но позволяет при этом быстро расти, осваивая и используя сложные приемы когда они потребуются.
Зачем вы так C# обижаете.
Симбиоз Java и C# это явно Kotlin.
java версия уже вот так выглядит:
public List<Product> getProducts()
{
return orders.stream().flatMap(Order::getProducts).collect(toList());
}
(и по-моему это сильно читабельнее)
collect(toList())это опечатка и должно быть
collect().toList()или действительно так странно?
collect(Collectors.toList())
параметр к методу collect — это то, во что собираем. Так что не так уж и странно.
Автор комментария видимо «статически импортнул» Collectors.toList — поэтому и выглядит странно
ArrayList <Product>
еще можно объяснить, то for (Order order : order)
вообще не скомпилируется.
return orders.stream().map(Order::getProducts).flatMap(List::stream).collect(toList());
или
return orders.stream().flatMap(o -> o.getProducts().stream()).collect(toList());
Судя по тому, что о нем известно из сети, это больше медиа-персона, чем серьезный девелопер, а название компании JFrog не говорит ничего, равно как и должность Developer advocate.
Я не являюсь профессионалом, код — хобби.
Вполне допускаю, что персона что-то из себя представляет а я что-то упустил, однако насколько стоит ссылаться на его высказывания при оценке ЯП Scala?
В scala-версии как раз метод и объявлен. Вы просто def
не заметили.
public Stream<Product> getProducts() {
// если уж совсем честно, то здесь должно быть вот так
// потому что стримы flatMap-ят только стримы, а getProducts()
// врядли возвращает Stream
return orders.stream().flatMap(o -> o.getProducts().stream());
}
Но основной смысл картинки был не в том, что java такой же лаконичный язык как скала, а в том, что исходная картинка пытается показать java-разработчика каким-то атавизмом, в то время как и компьютер у него в действительности такой же, да и язык достаточно современный, пусть и без " тайп-классов на имплиситах".
Еще вы как бы заранее завели метод Order::getProducts, а в Scala геттер создается на ходу.
Я последнее время предпочитаю org.immutables:value
, несколько удобнее в обращении, работает на том же уровне (annotations processor).
Supplier<List<Product>> products = () -> orders.stream().flatMap(e -> e.getProducts().stream()).collect(Collectors.toList());
Тот факт, что компания TypeSafe не смогла заработать на продвижении Scala, на мой взгляд говорит лишь о закостенелости рынка Enterprise разработки (Я здесь побуду капитаном-очевидностью), и не должна отпугивать разработчиков от изучения функциональных языков программирования.
В общем, статья скорее для менеджеров, чем для инженеров. А моё ИМХО: есть классный курс «Принципы функционального программирования на Scala», который ведёт сам Одерски.
И хоть меня закидают помидорами за такое мнение…
Большинству компаний не нужны ни Scala ни развите Java. Им это просто не выгодно. У них есть куча продуктов на старых версиях Java, которые кто-то должен поддерживать и продавать. Для этого нужны люди.
А как можно вообще заманить человека на унылую работу по поддержке легаси?
Ведь не секрет, что большая часть Java проектов до сих пор не вышли даже за Java 6.
Переписывать дорого и не факт что выйдет лучше чем было, плюс риски для клиентов.
Вот и культивируются мысли молодых разработчиков, что в Java все деньги, дергаться не надо, это типа большие риски. Плюс текущая ситуация на рынке, ЗП за легаси платят неплохие.
Все это вместе заставляет очень медленно проворачиваться колесо индустрии в целом.
У этой ситуации нет простого решения, но надо понимать, почему на Scala в основном катят бочку не в техническом плане.
Вот только никто почему-то не вспоминает про работу с асинхронностью с Future as monad из коробки.
Или про тайп-классы на имплиситах, которые позволяют писать какие угодно дженерик решения для любых
типов (да, да, даже для сторонних, стандартных, whatever, и всё это без оберток).
Или про type-safe парадигму в скале, когда, во многом благодаря той самой «переусложненной» системе типов, можно смотреть на List[Any] или касты .instanceOf как на недоразумение неопытного разработчика.
И да, работу на скалке тоже можно найти. И, что характерно, вероятно, что вы окажетесь в опытной и сильной команде, у которой всегда можно чему-то научиться, что должно быть, по моему мнению, решающим фактором на любой работе. И это по той простой причине, что порог вхождения в язык находится выше уровня «копипаст со stackoverflow, херак и в продакшн».
Имхо, тот график с удовлетворенностью разработчиков языком говорит больше, чем вся статья и холивар вокруг.
На моей памяти, это первый язык, который у меня не вызывает приступов ненависти даже спустя три года, что для любой технологии в наше непростое время — невиданное явление.
Или про тайп-классы на имплиситах, которые позволяют писать какие угодно дженерик решения для любых
типов (да, да, даже для сторонних, стандартных, whatever, и всё это без оберток).
Сколько у вас человек в команде?
Пишу на скале уже два года и ни за что не вернусь обратно. Даже если не лезть в систему типов и имплиситы, которые, имхо, вредны для ежедневного программирования, есть куча мелочей, которые в сумме дают сильный прирост скорости разработки. if/switch/code block as expressions, паттерн матчинг, однострочные объявления методов, сокращенный синтаксис вызова curried functions...
Быть может, когда-нибудь накатаю статью на тему "чем scala лучше с точки зрения промышленного программиста".
тем, что в джаве есть только CompletableFuture, который спроектирован, скажем так, неудачно. И даже если сделали (или сделают) нормальный порт на джаву, то тормозящим фактором станет уже синтаксис:
task1.thenCompose(v1 -> {
return task2.thenCompose(v2 -> {
return runTask3(v1 + v2);
});
});
и
task1.flatMap { v1 =>
task2.flatMap { v2 =>
runTask3(v1 + v2)
}
}
Быть может, это выглядит лишь немного симпатичнее, но для больших кусков кода разница становится существенной.
supplyAsync(() -> "test").thenCombineAsync(supplyAsync(() -> "test2"), this::doSomething);
В этом смысле на мой взгляд намного интереснее поддержка continuation-ов в kotlin 1.1, вероятно такой код можно было бы сделать:
val v1 = await(runTask1())
val v2 = await(runTask2())
runTask3(v1, v2)
supplyAsync(this::runTask1).thenCombineAsync(supplyAsync(this::runTask2), this::runTask3);
о чем я и говорю — если НЕ выносить каждую лямбду в отдельный метод, то получается взрыв скобочек.
// как предложил @Sirikid ниже
task1.thenCombine(task2, (v1, v2) -> runTask3(v1 + v2))
task1.thenCombine(task2, Integer::sum).thenCompose(this::runTask3);
по моему опыту использования scala, чаще всего её нужно или использовать как предлагают в best practice и тогда она становится достаточно сложной или java-way и тогда особые преимущества найти сложно, а местами даже скалу использовать менее удобно, например аннотации, которые несовместимы с java-аннотациями.
Ну как сказать… три разных синтаксиса для одной и той же задачи, причем все три специфичны для входных условий. одни хороши, когда у нас переменные, другие — когда функции, третьи — когда лямбды. Обратите внимание, в скале для этой задачи один и тот же синтаксис подходит для всех случаев, и этот синтаксис — прост.
И — откуда вообще мысль, что best practice — сложен? Посмотрите вот сюда: http://twitter.github.io/effectivescala/
Ничего страшного там нет.
Что я хочу сказать — предложенные варианты на джаве могут быть короче, могут быть интереснее, но они сложнее. И это при этом, что в сложности обвиняют скалу.
task1, task2 — это локальные переменные типа CompletableFuture<Integer>
v1, v2 — это, соответственно, Integer
CompletableFuture<Result> runTask3(int arg);
Раз уж пошли код сравнивать как можно без lisp в лице Clojure:
(defn task-3 []
(let [v1 (future (task-1))
v2 (future (task-2))]
(future (+ @v1 @v2))))
и
task1.thenCompose(v1 -> {
return task2.thenCompose(v2 -> {
return runTask3(v1 + v2);
});
});
плавно превращается в:
task1.thenCompose(v1 ->
task2.thenCompose(v2 ->
runTask3(v1 + v2)
)
);
for {
a <- futureA
b <- futureB
c <- futureC
} yield {
...
}
Компилятор сам разложит на map/flatMap (можно даже с if подключить filter). Это для начального уровня.
Зайдя чуть дальше в ФП — там Future всё еще монада (ну ладно, тут я лукавлю, она монада, если забыть про эксепшены внутри, иначе left identity не выполняется, но не суть). А значит, можно подрубить монад-трансформеры и работать с Future[Option[A]] словно у вас праздник на улице. В коде ниже мы получаем доступ сразу до значения типа А внутри двух монад:
for {
a <- futureOptA.optionT //a: A
b <- futureOptB.optionT //b: B
c <- futureOptC.optionT //c: C
} yield {
...
}
К слову, это довольно частый кейс при работе с асинхронными базами/драйверами. Вы ждете запись в будущем, которая может и не существовать в базе.
Любопытства ради, хотелось бы увидеть решение на джаве. Слышал, там Option тоже завезли.
Не знаю, у меня было совершенно наоборот. Когда меня бросили в скалу, я ее сначала возненавидел, зато потом полюбил. И не за синтаксис, и не за вывод типов, и не за лямбды. А за мелочи, которые делают жизнь проще: Правильным образом перегружаемый ==
инвариантные массивы, ADT (которые позволяют делать исчерпывающую проверку при паттерн-матчинге), Кейс-классы знатно экономят время. А потом уже стандатный набор всего и вся.
Например из цитаты platoff можно сделать вывод, что Scala выбирает неопытная молодежь, хотя на самом деле все с точностью до наоборот. Цитата Баруха тоже не к месту, с чего Вы взяли, что мнение человека из Java/Groovy-мира является экспертным относительно Scala?! (ничего не имею против Баруха, с удовольствием слушаю подскаст всей их тусовки «Разбор полетов», но у него нет экспириенса в этой нише и это просто мнение сбоку по факту). Приведенные рейтинги всяких Teobe, IEEE Spectrum и прочих вообще не отражают реальности: Matlab, R, Asembly выше в рейтинге чем Scala — ага, кулл стори… Судить о языке по количеству вбиваний в гугл и каких-то абстрактных статистик в вакууме — это полнейший бред.
Scala развивается громадными темпами, причем имеется в виду не только сам язык, но и экосистема и востребованность на рынке. Исходя из недавних отчетов stackoverflow.com scala-разработчики одни из самых довольных своей технологией + с одними из самых высоких заработков среди разработчиков. Похожие данные были и от Dou, которые тоже кстати напрямую работают с комьюнити(на Dou просто делаются опросы), а не как Teobe и КО, которые берут данные с потолка.
Создается ложное впечатление, что у Scala все плохо, но перспективы есть. Все совсем не так — для Scala эти перспективы уже настали, сейчас все отлично и продолжает набирать обороты. Эта статья была бы актуальна этак году в 2009ом, но ни как не в текущих тренда и спроса на биг-дейта, анализ данных, распределенных систем и прочего, в чем Scala преуспела.
В статье ни слова про уровень и количество конференций в ФП мире и в Scala в частности. Про положительный экспириенс фирм, которые используют Scala — недавно paypal выкладывали статью о том у них все отлично с Akka. Про крутые проекты/технологии, которые были реализованы на Scala — Spark, Akka, Kamon, Kafka и т.д. О том какие нововведения и перспективы ждут нас в ближайшем будущем — Scala 2.12, Dotty и прочее.
Спрос на Scala весьма хороший в текущих реалиях, о каких “разворотах в сторону Java” вообще идет речь? На Linkedin даже обычным Scala разработчикам постоянно приходят предложения, не говоря уже о Spark специалистах. Еще можно посмотреть тут и сделать выводы: https://jobs.functionalworks.com/
Вообще лучше смотреть кейноты ФП и Scala конференций, чтобы понимать что происходит в ФП и Scala-мире, а не из слов людей знающих о продакшене на Scala чуть меньше чем совсем ничего, набрасывающих и делающих выводы на основе Teobe-рейтингов и говорящих о “смерти” языка из-за обычного переименования Typesafe -> Lightbend…
Вот еще хорошее видео, с объективной оценкой о том “Какое место Scala занимает в IT-индустрии” — https://www.youtube.com/watch?v=jTomnoJ3TyQ
Это тоже плохой показатель. Он демонстрирует только наличие дефицита — но ничего не говорит о числе предложений.
Какое место занимает язык Scala в ИТ-индустрии