Название Scala происходит от слова scalable, и в соответствии с этим названием язык Scala используется для поддержки загруженных веб-сайтов и анализа огромных наборов данных. В этом разделе представлены функции, которые делают Scala масштабируемым языком. Эти функции разделены на три раздела:
- Функции высокоуровневого языка программирования
- Функции низкоуровневого языка программирования
- Особенности экосистемы Scala
Высокоуровневые функции
Глядя на Scala с пресловутого “вида с высоты 30 000 фунтов”, вы можете сделать о нем следующие утверждения:
- Это высокоуровневый язык программирования
- Он имеет краткий, читаемый синтаксис
- Он статически типизирован (но кажется динамичным)
- Имеет выразительную систему типов
- Это язык функционального программирования (ФП)
- Это язык объектно-ориентированного программирования (ООП)
- Он поддерживает слияние ФП и ООП
- Контекстные абстракции обеспечивают понятный способ реализации вывода терминов (term inference)
- Он работает на JVM (и в браузере)
- Беспрепятственно взаимодействует с Java кодом
- Он используется для серверных приложений (включая микросервисы), приложений для работы с большими данными, а также может использоваться в браузере с помощью Scala.js
Эти функции кратко рассматриваются в следующих разделах.
Высокоуровневый язык
Scala считается высокоуровневым языком как минимум по двум причинам. Во-первых, подобно Java и многим другим современным языкам, вы не имеете дело с низкоуровневыми понятиями, такими как указатели и управление памятью.
Во-вторых, с использованием лямбда-выражений и функций высшего порядка вы пишете свой код на очень высоком уровне. Как говорится в функциональном программировании, в Scala вы пишете то, что хотите, а не то, как этого добиться. То есть мы не пишем императивный код вот так:
import scala.collection.mutable.ListBuffer
def double(ints: List[Int]): List[Int] = {
val buffer = new ListBuffer[Int]()
for (i <- ints) {
buffer += i * 2
}
buffer.toList
}
val oldNumbers = List(1, 2, 3)
val newNumbers = double(oldNumbers)
import scala.collection.mutable.ListBuffer
def double(ints: List[Int]): List[Int] =
val buffer = new ListBuffer[Int]()
for i <- ints do
buffer += i * 2
buffer.toList
val oldNumbers = List(1, 2, 3)
val newNumbers = double(oldNumbers)
Этот код шаг за шагом указывает компилятору, что делать. Вместо этого мы пишем высокоуровневый функциональный код, используя функции высшего порядка и лямбда-выражения, подобные этому, для вычисления того же результата:
val newNumbers = oldNumbers.map(_ * 2)
Как видите, этот код намного лаконичнее, его легче читать и легче поддерживать.
Лаконичный синтаксис
Scala имеет краткий, удобочитаемый синтаксис. Например, переменные создаются лаконично, а их типы понятны:
val nums = List(1,2,3)
val p = Person("Martin", "Odersky")
Функции высшего порядка и лямбда-выражения делают код кратким и удобочитаемым:
nums.map(i => i * 2) // длинная форма
nums.map(_ * 2) // краткая форма
nums.filter(i => i > 1)
nums.filter(_ > 1)
Трэйты, классы и методы определяются с помощью простого и легкого синтаксиса:
trait Animal {
def speak(): Unit
}
trait HasTail {
def wagTail(): Unit
}
class Dog extends Animal with HasTail {
def speak(): Unit = println("Woof")
def wagTail(): Unit = println("⎞⎜⎛ ⎞⎜⎛")
}
trait Animal:
def speak(): Unit
trait HasTail:
def wagTail(): Unit
class Dog extends Animal, HasTail:
def speak(): Unit = println("Woof")
def wagTail(): Unit = println("⎞⎜⎛ ⎞⎜⎛")
Исследования показали, что время, которое разработчик тратит на чтение и написание кода, составляет как минимум 10:1, поэтому важно писать краткий и читабельный код.
Ощущение динамики
Scala — это язык со статической типизацией, но благодаря своим возможностям вывода типов он кажется динамичным. Все эти выражения выглядят как языки с динамической типизацией, такие как Python или Ruby, но это все Scala:
val s = "Hello"
val p = Person("Al", "Pacino")
val sum = nums.reduceLeft(_ + _)
val y = for (i <- nums) yield i * 2
val z = nums
.filter(_ > 100)
.filter(_ < 10_000)
.map(_ * 2)
val s = "Hello"
val p = Person("Al", "Pacino")
val sum = nums.reduceLeft(_ + _)
val y = for i <- nums yield i * 2
val z = nums
.filter(_ > 100)
.filter(_ < 10_000)
.map(_ * 2)
Как утверждает Heather Miller, Scala считается сильным языком со статической типизацией, и вы получаете все преимущества статических типов:
- Корректность: вы обнаруживаете большинство ошибок во время компиляции
- Отличная поддержка IDE
- Надежное автодополнение кода
- Отлов ошибок во время компиляции означает отлов ошибок по мере написания
- Простой и надежный рефакторинг
- Вы можете уверенно рефакторить свой код
- Объявления типов методов сообщают читателям, что делает метод, и помогают служить документацией
- Масштабируемость и удобство обслуживания: типы помогают обеспечить корректность в произвольно больших приложениях и командах разработчиков
- Строгая типизация в сочетании с превосходным выводом типов позволяет использовать такие механизмы, как контекстная абстракция, которая позволяет вам опускать шаблонный код. Часто этот шаблонный код может быть выведен компилятором на основе определений типов и заданного контекста.
Выразительная система типов
Система типов в Scala во время компиляции обеспечивает безопасное и согласованное использование абстракций. В частности, система типов поддерживает:
- Выводимые типы
- Generic классы
- Аннотации вариантности
- Верхняя и нижняя границы типов
- Полиморфные методы
- Типы пересечения
- Типы объединения
- Лямбда-типы
- Экземпляры
given
и предложенияusing
- Методы расширения
- Типовые классы
- Многостороннее равенство
- Псевдонимы непрозрачного типа
- Открытые классы
- Типы соответствия
- Зависимые типы функций
- Полиморфные функциональные типы
- Контекстные границы
- Контекстные функции
- Внутренние классы и элементы абстрактного типа как элементы объекта
В сочетании эти функции обеспечивают мощную основу для безопасного повторного использования программных абстракций и для безопасного расширения программного обеспечения.
Язык функционального программирования
Scala — это язык функционального программирования (ФП), что означает:
- Функции — это значения, и их можно передавать, как и любое другое значение
- Напрямую поддерживаются функции высшего порядка
- Встроенные лямбда
- Все в Scala — это выражение, возвращающее значение
- Синтаксически легко использовать неизменяемые переменные, и их использование приветствуется
- В стандартной библиотеке языка содержится множество неизменяемых классов коллекций
- Эти классы коллекций поставляются с десятками функциональных методов: они не изменяют коллекцию, вместо этого возвращая обновленную копию данных
Объектно-ориентированный язык
Scala — это язык объектно-ориентированного программирования (ООП). Каждое значение — это экземпляр класса, а каждый “оператор” — это метод.
В Scala все типы наследуются от класса верхнего уровня Any
, чьими непосредственными дочерними элементами являются AnyVal
(типы значений, такие как Int
и Boolean
) и AnyRef
(ссылочные типы, как в Java).
Это означает, что различие в Java между примитивными и упакованными типами (например, int
против Integer
) отсутствует в Scala.
Упаковка и распаковка полностью прозрачны для пользователя.
Поддерживает слияние ФП/ООП
Суть Scala заключается в слиянии функционального программирования и объектно-ориентированного программирования в типизированной среде:
- Функции для логики
- Объекты для модульности
Как заявил Мартин Одерски, “Scala был разработан, чтобы показать, что слияние функционального и объектно-ориентированного программирования возможно и практично”.
Вывод терминов стал более понятным
После Haskell Scala был вторым популярным языком, в котором была некоторая форма неявных (implicits) выражений. В Scala 3 эти концепции были полностью переосмыслены и реализованы более четко.
Основная идея заключается в выводе терминов: на основе заданного, компилятор синтезирует “канонический” термин, который имеет этот тип. В Scala параметр контекста напрямую ведет к выводимому термину аргумента, который также может быть записан явно.
Примеры использования этой концепции включают реализацию типовых классов, установление контекста, внедрение зависимостей, выражение возможностей, вычисление новых типов и доказательство отношений между ними.
Scala 3 делает этот процесс более понятным, чем когда-либо прежде. О контекстных абстракциях можно прочесть в Справочной документации.
Клиент & сервер
Код Scala работает на виртуальной машине Java (JVM), поэтому вы получаете все ее преимущества:
- Безопасность
- Производительность
- Управление памятью
- Портативность и независимость от платформы
- Возможность использовать множество существующих Java и JVM библиотек
Помимо работы на JVM, Scala также работает в браузере с помощью Scala.js (и сторонних инструментов с открытым исходным кодом для интеграции популярных библиотек JavaScript), а собственные исполняемые файлы могут быть созданы с помощью Scala Native и GraalVM.
Беспрепятственное взаимодействие с Java
Вы можете использовать Java классы и библиотеки в своих приложениях Scala, а также код Scala в приложениях Java. Что касается второго пункта, большие библиотеки, такие как Akka и Play Framework написаны на Scala и могут использоваться в приложениях Java.
Что касается первого пункта, классы и библиотеки Java используются в приложениях Scala каждый день.
Например, в Scala вы можете читать файлы с помощью BufferedReader
и FileReader
из Java:
import java.io.*
val br = BufferedReader(FileReader(filename))
// чтение файла в `br` ...
Использование Java-кода в Scala, как правило, не вызывает затруднений.
В Scala также можно использовать коллекции Java, и если вы хотите использовать с ними богатый набор методов классов коллекций Scala, то можете преобразовать их с помощью всего нескольких строк кода:
import scala.jdk.CollectionConverters.*
val scalaList: Seq[Integer] = JavaClass.getJavaList().asScala.toSeq
Богатство библиотек
Как будет видно в третьем разделе этой страницы, библиотеки и фреймворки Scala, подобные нижеследующим, были написаны для поддержки загруженных веб-сайтов и работы с огромными наборами данных:
- Play Framework — это легкая, без сохранения состояния, удобная для web, удобная для разработчиков архитектура для создания масштабируемых приложений
- Apache Spark — это унифицированный аналитический механизм для обработки больших данных со встроенными модулями для потоковой передачи, SQL, машинного обучения и обработки графиков
В списке Awesome Scala представлены десятки дополнительных инструментов с открытым исходным кодом, созданных разработчиками для создания приложений Scala.
В дополнение к программированию на стороне сервера, Scala.js представляет собой строго типизированную замену для написания JavaScript со сторонними библиотеками с открытым исходным кодом, которые включают инструменты для интеграции с библиотекой Facebook React, jQuery и т.д.
Функции низкоуровневого языка
Хотя в предыдущем разделе были рассмотрены высокоуровневые функции Scala, интересно отметить, что на высоком уровне вы можете делать одни и те же утверждения как о Scala 2, так и о Scala 3. Десять лет назад Scala начиналась с прочного фундамента желаемых функций, и вы увидите в этом разделе, что в Scala 3 эти преимущества были улучшены.
С точки зрения деталей “на уровне моря” — то есть функций языка, которые программисты используют каждый день — Scala 3 имеет значительные преимущества по сравнению со Scala 2:
- Возможность более лаконично создавать алгебраические типы данных (ADT) с перечислениями
- Еще более лаконичный и читаемый синтаксис:
- Синтаксис “тихой” структуры управления легче читать
- Опциональные фигурные скобки
- Меньшее количество символов в коде создает меньше визуального шума, что упрощает его чтение
- Ключевое слово
new
обычно больше не требуется при создании экземпляров класса - Формальность объектов пакета была заменена более простыми определениями “верхнего уровня”
- Более понятная грамматика:
- Несколько различных вариантов использования ключевого слова
implicit
были удалены; это использование заменено более очевидными ключевыми словами, такими какgiven
,using
, иextension
, фокусирующихся на намерении, а не механизме (подробности см. в разделе Givens) - Методы расширения заменяют неявные классы более понятным и простым механизмом
- Добавление модификатора
open
для классов заставляет разработчика намеренно объявить, что класс открыт для модификации, тем самым ограничивая специальные расширения кодовой базы - Многостороннее равенство исключает бессмысленные сравнения с
==
и!=
(т.е. попытки сравнитьPerson
сPlanet
) - Гораздо проще реализуются макросы
- Объединение и пересечение предлагают гибкий способ моделирования типов
- Параметры трейтов заменяют и упрощают ранние инициализаторы
- Псевдонимы непрозрачных типов заменяют большинство случаев использования классов значений, гарантируя при этом отсутствие упаковки
- Export предложения обеспечивают простой и общий способ выражения агрегации, который может заменить предыдущий шаблон фасада объектов пакета, наследуемых от классов
- Синтаксис procedure был удален, а синтаксис varargs - изменен, чтобы сделать язык более согласованным
@infix
аннотация делает очевидным желаемое применение метода- Аннотация метода
@targetName
определяет альтернативное имя метода, улучшая совместимость с Java и позволяя указывать псевдонимы для символических операторов
- Несколько различных вариантов использования ключевого слова
Демонстрация всех этих функций заняла бы слишком много места, но перейдите по ссылкам в пунктах выше, чтобы увидеть эти функции в действии. Все эти функции подробно обсуждаются на страницах New, Changed и Dropped функций в обзорной документации.
Экосистема Scala
У Scala динамичная экосистема с библиотеками и фреймворками под любые требования. Список “Awesome Scala” содержит список сотен проектов с открытым исходным кодом, доступных разработчикам Scala, а Scaladex предоставляет доступный для поиска индекс библиотек Scala. Некоторые из наиболее известных библиотек перечислены ниже.
Web разработка
- Play Framework следует модели Ruby on Rails, чтобы стать легкой, не сохраняющей состояния, удобной для разработчиков и web архитектурой для высокомасштабируемых приложений
- Scalatra — небольшой высокопроизводительный асинхронный web framework, вдохновленный Sinatra
- Finatra — это сервисы Scala, построенные на TwitterServer и Finagle
- Scala.js — это строго типизированная замена JavaScript, обеспечивающая более безопасный способ создания надежных интерфейсных web-приложений
- ScalaJs-React поднимает библиотеку Facebook React на Scala.js и пытается сделать ее максимально безопасной для типов и удобной для Scala
HTTP(S) библиотеки:
JSON библиотеки:
Сериализация:
Наука и анализ данных:
Большие данные
ИИ, машинное обучение
- BigDL (Распределенная среда глубокого обучения для Apache Spark)
- TensorFlow Scala
Функциональное программирование & Функциональное реактивное программирование
ФП:
Функциональное реактивное программирование (ФРП):
Инструменты сборки
Подведем итоги
Как показано на этой странице, Scala обладает множеством замечательных функций высокоуровневого языка программирования, низкоуровневого языка программирования и богатой экосистемой разработчиков.
Contributors to this page:
Contents
- Введение
- Возможности Scala
- Почему Scala 3?
- Почувствуй Scala
- Пример 'Hello, World!'
- REPL
- Переменные и типы данных
- Структуры управления
- Моделирование данных
- Методы
- Функции первого класса
- Одноэлементные объекты
- Коллекции
- Контекстные абстракции
- Верхнеуровневые определения
- Обзор
- Первый взгляд на типы
- Интерполяция строк
- Структуры управления
- Моделирование предметной области
- Инструменты
- Моделирование ООП
- Моделирование ФП
- Методы
- Особенности методов
- Main методы в Scala 3
- Обзор
- Функции
- Анонимные функции
- Параметры функции
- Eta расширение
- Функции высшего порядка
- Собственный map
- Создание метода, возвращающего функцию
- Обзор
- Пакеты и импорт
- Коллекции в Scala
- Типы коллекций
- Методы в коллекциях
- Обзор
- Функциональное программирование
- Что такое функциональное программирование?
- Неизменяемые значения
- Чистые функции
- Функции — это значения
- Функциональная обработка ошибок
- Обзор
- Типы и система типов
- Определение типов
- Параметризованные типы
- Пересечение типов
- Объединение типов
- Алгебраические типы данных
- Вариантность
- Непрозрачные типы
- Структурные типы
- Зависимые типы функций
- Другие типы
- Контекстные абстракции
- Методы расширения
- Параметры контекста
- Контекстные границы
- Given импорты
- Классы типов
- Многостороннее равенство
- Неявное преобразование типов
- Обзор
- Параллелизм
- Scala утилиты
- Сборка и тестирование проектов Scala с помощью Sbt
- Рабочие листы
- Взаимодействие с Java