Несмотря на то, что по Scala достаточно множество книжек для начинающих, литературы уровня "advanced" не очень много. Однако год назад вышла книга Дениса Калинина, содержащая продвинутые рекомендации по программированию на Scala с прикладным уклоном (включает в том числе начала теории категорий).
https://leanpub.com/mastering-advanced-scala
https://leanpub.com/mastering-advanced-scala
Leanpub
Mastering Advanced Scala
A concise overview of purely functional programming in Scala with real-life examples from existing libraries
Примерно на том же уровне находится книга
https://underscore.io/books/scala-with-cats/
Scala with cats
, объясняющая, наоборот, общие абстракции из библиотеки cats
. Книг на самом деле две - вторая представляет из себя сборник упражнений по материалу первой.https://underscore.io/books/scala-with-cats/
underscore.io
Scala with Cats
Dive deep into functional patterns using Scala and Cats.
For experienced Scala developers.
For experienced Scala developers.
Данная библиотека - наглядный пример того, как Scala можно использовать для написания действительно нужных dsl, в данном случае для уже богатой возможностями библиотеки для работы с БД
https://github.com/Hydrospheredata/typed-sql
doobie
. Несмотря на плюсы в виде гарантированной статической типизации и удобной композиции запросов в транзакции, doobie
обладает достаточно серьёзным недостатком - отсутствием удобных инструментов для создания запросов, вследствие чего их приходится писать вручную, либо создавать собственное решение. Представленный dsl позволяет сохранить все преимущества doobie
и вместе с тем избавить программистов от необходимости использования raw-SQL запросов.https://github.com/Hydrospheredata/typed-sql
GitHub
GitHub - Hydrospheredata/typed-sql
Contribute to Hydrospheredata/typed-sql development by creating an account on GitHub.
Скорее всего, идея сделать Haskell на jvm назревала ещё до первого стабильного релиза оного, но до сих пор подобные языки явно не выходили из стадии экспериментальных проектов. А теперь такой язык есть -
На сайте проекта можно ознакомиться с синтаксисом и документацией языка, а также пройти ознакомительный тур. Для тех, кто уже созрел писать на
https://eta-lang.org/docs/user-guides/eta-user-guide/introduction/what-is-eta
Eta
, самый настоящий Haskell в экосистеме java-машины. В числе главных черт есть даже совместимость с Java!На сайте проекта можно ознакомиться с синтаксисом и документацией языка, а также пройти ознакомительный тур. Для тех, кто уже созрел писать на
Eta
в IDE, есть плагин в IDEA от создателей языка.https://eta-lang.org/docs/user-guides/eta-user-guide/introduction/what-is-eta
eta-lang.org
Introduction
Documentation of Eta Programming Language.
Короткий туториал по одной из самых быстрых Scala-библиотек для асинхронных задач
https://vimeo.com/299313903
monix
от её создателя. Библиотека обладает богатым инструментарием для композиции задач, смены контекста их выполнения, контроля выделенных ресурсов и шедулинга. В видео рассмотрены ключевые функции и подходы к управлению ресурсами с использованием monix
.https://vimeo.com/299313903
Vimeo
Monix: Task's Bracket, Resource and Streaming
A tutorial on usage of Task.bracket, Cats-Effect’s Resource and how that can be used in combination with streaming data types like Observable and Iterant. Canonical…
Алгебры являются одним из наиболее важных понятий в функциональном программировании, имеющих при этом непосредственную реализацию в коде программы. В рамках данной лекции рассматриваются основные виды алгебр и примеры их использования в проектах.
https://www.youtube.com/watch?v=s2ay9nEW3ak
https://www.youtube.com/watch?v=s2ay9nEW3ak
YouTube
Fun(c) 2018.4: Adam Rosien - why do Functional Programmers always talk about Algebra(s)?
Learn about what an algebra is, why functional programmers talk about them constantly, and how you can use them in your projects. Algebras *are* structure, and we'll talk about their various forms: algebraic data types, F-algebras, object algebras, and more!…
Несмотря на то, что ФП-концепции естественным образом ложатся на преобразования абстрактных данных и композицию вычислений, с нуля сложно представить, каким образом с помощью монад и алгебр можно сделать типовой веб-сервис, взаимодействующий с БД.
Хорошим примером являются следующие два sample-проекта:
https://github.com/jaspervz/todo-http4s-doobie
https://github.com/pauljamescleary/scala-pet-store
Хорошим примером являются следующие два sample-проекта:
Todo service
и Scala Pet store
, написанные в достаточно строгом функциональном стиле.https://github.com/jaspervz/todo-http4s-doobie
https://github.com/pauljamescleary/scala-pet-store
GitHub
GitHub - jaspervz/todo-http4s-doobie: A sample project of a microservice using http4s, doobie, and circe.
A sample project of a microservice using http4s, doobie, and circe. - jaspervz/todo-http4s-doobie
Достаточно интересная по концепции библиотека, позволяющая создание спецификации кода как композиции текста и иллюстративных примеров/тестов.
https://github.com/etorreborre/specs2
specs2
обладает очень большой описательной способностью благодаря встроенному интерполятору, также присутствует интеграция с библиотеками Mockito
и Scalacheck
.https://github.com/etorreborre/specs2
GitHub
GitHub - etorreborre/specs2: Software Specifications for Scala
Software Specifications for Scala. Contribute to etorreborre/specs2 development by creating an account on GitHub.
Развлекательной Скалы пост: существует твиттер (и соответствующий сайт) с высококачественными мемами на тему языков Scala и Haskell. Помимо, непосредственно, мемов в подборке встречаются также картинки-напоминания с основными особенностями (type-)классов библиотек
https://impurepics.com/
cats
, cats-effect
и fs2
. https://impurepics.com/
Impurepics
impurepics - index
impurepics, all kinds of impure pics
Одним из наиболее важных качеств функциональных языков является их лаконичность и выразительность, что позволяет сконцентрироваться на решении задачи, а не на синтаксисе языка.
Рассмотрим, к примеру, классическую задачу получения n первых чисел в последовательности Фибоначчи и вывода их на экран.
(Важно: в задаче не учитываются ограничения на размер численных типов данных - на самом деле даже
Вариант №1 - императивный (Java <6).
Всё просто - классическое создание массива необходимого размера и его последующее заполнение. При этом требуется дополнительная логика проверки входных значений.
Печать в консоль также производится с помощью итерации по элементам массива.
Примечательно, что Java позволяет создавать массивы нулевой длины, так что этого типа данных достаточно для выполнения данной задачи.
https://gist.github.com/J0kerPanda/8ff3475b10bdc4e7a986322daf94a90c
Вариант №2 - рекурсивный (Java 8).
Перепишем данную функцию с использованием рекурсии. Поскольку для итераций необходим аккуммулятор значений, необходимо
вынести рекурсивную логику в отдельную вспомогательную функцию. В связи с тем, что Java не позволяет объявлять вложенные функции,
её необходимо объявить на том же уровне, что и основную, что затрудняет чтение кода. Сама функция, несмотря на рекурсивную природу,
слабо отличается от предыдущей в виду аналогичной логики, построенной на
поэтому при больших
Стоит также отметить, что данная функция возвращает список в обратном порядке, поэтому, при необходимости, его необходимо инвертировать.
Для печати используется метод класса
https://gist.github.com/J0kerPanda/c0057ef5d9c823f47ef068a822946f67
Вариант №3 - рекурсивный (Scala).
В данном случае, для непосредственного вычисления списка чисел Фибоначчи используется внутренняя функция
Она хорошо отражает рекурсивную структуру базового алгоритма, а вставка начальных значений органично объединяется с основным случаем при помощи pattern-матчинга.
В данном случае вспомогательная функция изолирована от остального кода. При этом, поскольку Scala оптимизирует хвосторекурсивные вызовы, эффективность работы
данной функции сопоставима с циклом.
Числа Фибоначчи, как и в предыдущем случае, следуют в обратном порядке, а вывод также использует метод
https://gist.github.com/J0kerPanda/8e965ef34003ce3bcb23da98f27d8adc
Вариант №4 - потоковый (Scala)
Предыдущие примеры были написаны целиком с использованием функций для генерации списков чисел Фибоначчи. Однако, если вспомнить о математической природе задачи,
эти числа представляют собой бесконечную последовательность, подчинённую определённому закону. Естественным способом реализации бесконечных последовательностей
в функциональном программировании являются потоки (Stream).
Если представить себе поток пар чисел Фиббоначи (числа в паре упорядочены по возрастанию), то каждая последующая пара может быть получена из предыдущей из
наибольшего элемента предыдущей пары и суммы элементов предыдущей пары. Это, в свою очередь, позволяет получать необходимый список n чисел Фибоначчи как взятие n
элементов данного потока с последующей декомпозицией каждой пары в свой наибольший элемент. Код для получения такого потока занимает одну строчку.
Можно ли ещё короче? Haskell позволяет уложиться в 27 символов, но этот код менее понятен, чем предыдущий вариант, и требует понимания работы как библиотечной функции
Рассмотрим, к примеру, классическую задачу получения n первых чисел в последовательности Фибоначчи и вывода их на экран.
(Важно: в задаче не учитываются ограничения на размер численных типов данных - на самом деле даже
long
исчерпывается достаточно быстро (~92 число Фибоначчи)).Вариант №1 - императивный (Java <6).
Всё просто - классическое создание массива необходимого размера и его последующее заполнение. При этом требуется дополнительная логика проверки входных значений.
Печать в консоль также производится с помощью итерации по элементам массива.
Примечательно, что Java позволяет создавать массивы нулевой длины, так что этого типа данных достаточно для выполнения данной задачи.
https://gist.github.com/J0kerPanda/8ff3475b10bdc4e7a986322daf94a90c
Вариант №2 - рекурсивный (Java 8).
Перепишем данную функцию с использованием рекурсии. Поскольку для итераций необходим аккуммулятор значений, необходимо
вынести рекурсивную логику в отдельную вспомогательную функцию. В связи с тем, что Java не позволяет объявлять вложенные функции,
её необходимо объявить на том же уровне, что и основную, что затрудняет чтение кода. Сама функция, несмотря на рекурсивную природу,
слабо отличается от предыдущей в виду аналогичной логики, построенной на
if
, else
. При этом, Java не оптимизирует хвостовую рекурсию,поэтому при больших
n
вызов данной функции приведёт к переполнению стэка, чего не случилось бы в случае с циклом. Стоит также отметить, что данная функция возвращает список в обратном порядке, поэтому, при необходимости, его необходимо инвертировать.
Для печати используется метод класса
List
- forEach
, применяющий функцию System.out::println
, переданную в качестве аргумента, к каждому значению в списке.https://gist.github.com/J0kerPanda/c0057ef5d9c823f47ef068a822946f67
Вариант №3 - рекурсивный (Scala).
В данном случае, для непосредственного вычисления списка чисел Фибоначчи используется внутренняя функция
inner
, полностью зависящая только от своих аргументов.Она хорошо отражает рекурсивную структуру базового алгоритма, а вставка начальных значений органично объединяется с основным случаем при помощи pattern-матчинга.
В данном случае вспомогательная функция изолирована от остального кода. При этом, поскольку Scala оптимизирует хвосторекурсивные вызовы, эффективность работы
данной функции сопоставима с циклом.
Числа Фибоначчи, как и в предыдущем случае, следуют в обратном порядке, а вывод также использует метод
foreach
.https://gist.github.com/J0kerPanda/8e965ef34003ce3bcb23da98f27d8adc
Вариант №4 - потоковый (Scala)
Предыдущие примеры были написаны целиком с использованием функций для генерации списков чисел Фибоначчи. Однако, если вспомнить о математической природе задачи,
эти числа представляют собой бесконечную последовательность, подчинённую определённому закону. Естественным способом реализации бесконечных последовательностей
в функциональном программировании являются потоки (Stream).
Если представить себе поток пар чисел Фиббоначи (числа в паре упорядочены по возрастанию), то каждая последующая пара может быть получена из предыдущей из
наибольшего элемента предыдущей пары и суммы элементов предыдущей пары. Это, в свою очередь, позволяет получать необходимый список n чисел Фибоначчи как взятие n
элементов данного потока с последующей декомпозицией каждой пары в свой наибольший элемент. Код для получения такого потока занимает одну строчку.
val s = 1 #:: Stream.iterate((1, 1)) { case (n1, n2) => (n2, n1 + n2) }.map(_._2)Вариант №5 - потоковый (Haskell)
...
s.take(n).foreach(println)
Можно ли ещё короче? Haskell позволяет уложиться в 27 символов, но этот код менее понятен, чем предыдущий вариант, и требует понимания работы как библиотечной функции
scanl
, так и самого Haskell.fibs = 1 : scanl (+) 1 fibs
Какой подход лучше?
anonymous poll
Потоковый (Scala) – 8
👍👍👍👍👍👍👍 73%
Императивный (Java) – 1
👍 9%
Рекурсивный (Java) – 1
👍 9%
Потоковый (Haskell) – 1
👍 9%
Рекурсивный (Scala)
▫️ 0%
👥 11 people voted so far.
anonymous poll
Потоковый (Scala) – 8
👍👍👍👍👍👍👍 73%
Императивный (Java) – 1
👍 9%
Рекурсивный (Java) – 1
👍 9%
Потоковый (Haskell) – 1
👍 9%
Рекурсивный (Scala)
▫️ 0%
👥 11 people voted so far.
Поскольку новая заметка вышла достаточно объёмной, да и переключаться на код из телеграма не очень удобно, решил попробовать следующий формат - всё, что не умещается в одно телеграм-сообщение публиковать на сайте, а мелкие заметки оставить в рамках канала.
Посмотреть на то, что получилось, можно по ссылке:
https://j0kerpanda.github.io/2018/11/24/algebraic-data-types.html
Посмотреть на то, что получилось, можно по ссылке:
https://j0kerpanda.github.io/2018/11/24/algebraic-data-types.html
Scala bin
Алгебраические типы данных
Сегодня разговор пойдёт об алгебраических типах данных - одной из ключевых концепций в функциональных языках.
Насколько подходит новый формат?
anonymous poll
Всё как надо - статьи в блог, мелкие заметки в канал – 6
👍👍👍👍👍👍👍 100%
Новый формат нужен, но текущий вариант не подходит
▫️ 0%
Канал подходит для всего, ничего больше не нужно
▫️ 0%
👥 6 people voted so far.
anonymous poll
Всё как надо - статьи в блог, мелкие заметки в канал – 6
👍👍👍👍👍👍👍 100%
Новый формат нужен, но текущий вариант не подходит
▫️ 0%
Канал подходит для всего, ничего больше не нужно
▫️ 0%
👥 6 people voted so far.
Отличная заметка про "best-practices", как в Scala, так и вообще в программировании. Также упоминаются достаточно интересные тонкости языка - готов поспорить, вы не знали, что явный
https://github.com/alexandru/scala-best-practices/blob/master/sections/2-language-rules.md
return
во вложенной анонимной функции реализован через выкидывание исключения (поэтому никогда не используйте его в Scala). https://github.com/alexandru/scala-best-practices/blob/master/sections/2-language-rules.md
GitHub
scala-best-practices/sections/2-language-rules.md at master · alexandru/scala-best-practices
A collection of Scala best practices. Contribute to alexandru/scala-best-practices development by creating an account on GitHub.
К разговору об особенностях языка: многие знают, что
Устраняет эти проблемы scala-plugin от Олега Пыжова, который часто подключают в проекты по умолчанию.
Больше примеров
for-comprehensions
в Scala - просто синтаксический сахар вокруг map
и flatMap
. Тем не менее, важно помнить, что каждая такая операция - это дополнительная аллокация (а в for-comprehensions
по умолчанию добавляется дополнительный map
в конце). При этом любая декомпозиция объектов (например, for { Dummy(v) <- Some(Dummy(v)) } yield v
) требует наличия метода withFilter
, который не для каждой структуры может быть реализован в принципе.Устраняет эти проблемы scala-plugin от Олега Пыжова, который часто подключают в проекты по умолчанию.
Больше примеров
for-comprehensions
в Scala тут и тут.GitHub
GitHub - oleg-py/better-monadic-for: Desugaring scala `for` without implicit `withFilter`s
Desugaring scala `for` without implicit `withFilter`s - oleg-py/better-monadic-for
Довольно часто в сигнатурах функций и классов в Scala можно увидеть что-то вроде
def func[F[_]](...)
, что у многих поначалу вызывает затруднения при чтении соответствующего кода. Данная конструкция делает следующий шаг в параметризации типов: func[F[_]](...)
- это функция, параметризованная типом, который, в свою очередь, тоже зависит от некоторого типового параметра. Предпосылки этой абстракции лаконично (особенно для первого знакомства) описаны в следующей статье.Medium
Understanding F[_] in Scala
Climbing up the ladder of abstraction
На правах неоплачиваемой рекламы: достаточно большое число людей начинает знакомство со Scala с курсов на
https://hub.mybinder.org/user/sbrunk-almond-examples-3zsmyq6n/lab
Сoursera
или книжек от Мартина Одерски. Теперь появился ещё один весьма интересный способ приступить к изучению языка - коллекция интерактивных примеров на Jupyter
. Для взаимодействия с ними нужен только браузер, а сам материал материал подан очень качественно и подробно.https://hub.mybinder.org/user/sbrunk-almond-examples-3zsmyq6n/lab
Для более опытных скалистов также вскоре откроется новый курс, в создании которого принял участие уже упомянутый Мартин Одерски - создатель языка Scala. Онлайн-курс начинается в феврале и посвящен реактивному программированию. Он включает в себя как общие концепции, так и их реализацию на Scala с помощью фреймворка
https://www.edx.org/course/programming-reactive-systems
Akka
. Изучение материалов абсолютно бесплатно, однако за скромные 50$ можно проспонсировать создателей курса и платформу, а также получить приятный сертификат в резюме. https://www.edx.org/course/programming-reactive-systems
edX
EPFLx: Programming Reactive Systems | edX
Principles of Reactive Programming in Scala.