Skip to content

Latest commit

 

History

History
485 lines (351 loc) · 30.3 KB

scala-features.md

File metadata and controls

485 lines (351 loc) · 30.3 KB
layout title scala3 partof overview-name type description language num previous-page next-page
multipage-overview
Возможности Scala
true
scala3-book
Scala 3 — Book
chapter
На этой странице рассматриваются основные возможности языка программирования Scala.
ru
2
introduction
why-scala-3

Название Scala происходит от слова scalable, и в соответствии с этим названием язык Scala используется для поддержки загруженных веб-сайтов и анализа огромных наборов данных. В этом разделе представлены функции, которые делают Scala масштабируемым языком. Эти функции разделены на три раздела:

  • Функции высокоуровневого языка программирования
  • Функции низкоуровневого языка программирования
  • Особенности экосистемы Scala

Высокоуровневые функции

Глядя на Scala с пресловутого “вида с высоты 30 000 фунтов”, вы можете сделать о нем следующие утверждения:

  • Это высокоуровневый язык программирования
  • Он имеет краткий, читаемый синтаксис
  • Он статически типизирован (но кажется динамичным)
  • Имеет выразительную систему типов
  • Это язык функционального программирования (ФП)
  • Это язык объектно-ориентированного программирования (ООП)
  • Он поддерживает слияние ФП и ООП
  • Контекстные абстракции обеспечивают понятный способ реализации вывода терминов (term inference)
  • Он работает на JVM (и в браузере)
  • Беспрепятственно взаимодействует с Java кодом
  • Он используется для серверных приложений (включая микросервисы), приложений для работы с большими данными, а также может использоваться в браузере с помощью Scala.js

Эти функции кратко рассматриваются в следующих разделах.

Высокоуровневый язык

Scala считается высокоуровневым языком как минимум по двум причинам. Во-первых, подобно Java и многим другим современным языкам, вы не имеете дело с низкоуровневыми понятиями, такими как указатели и управление памятью.

Во-вторых, с использованием лямбда-выражений и функций высшего порядка вы пишете свой код на очень высоком уровне. Как говорится в функциональном программировании, в Scala вы пишете то, что хотите, а не то, как этого добиться. То есть мы не пишем императивный код вот так:

{% tabs scala-features-1 class=tabs-scala-version %} {% tab 'Scala 2' for=scala-features-1 %}

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)

{% endtab %} {% tab 'Scala 3' for=scala-features-1 %}

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)

{% endtab %} {% endtabs %}

Этот код шаг за шагом указывает компилятору, что делать. Вместо этого мы пишем высокоуровневый функциональный код, используя функции высшего порядка и лямбда-выражения, подобные этому, для вычисления того же результата:

{% tabs scala-features-2 %} {% tab 'Scala 2 и 3' for=scala-features-2 %}

val newNumbers = oldNumbers.map(_ * 2)

{% endtab %} {% endtabs %}

Как видите, этот код намного лаконичнее, его легче читать и легче поддерживать.

Лаконичный синтаксис

Scala имеет краткий, удобочитаемый синтаксис. Например, переменные создаются лаконично, а их типы понятны:

{% tabs scala-features-3 %} {% tab 'Scala 2 и 3' for=scala-features-3 %}

val nums = List(1,2,3)
val p = Person("Martin", "Odersky")

{% endtab %} {% endtabs %}

Функции высшего порядка и лямбда-выражения делают код кратким и удобочитаемым:

{% tabs scala-features-4 %} {% tab 'Scala 2 и 3' for=scala-features-4 %}

nums.map(i => i * 2)   // длинная форма
nums.map(_ * 2)        // краткая форма

nums.filter(i => i > 1)
nums.filter(_ > 1)

{% endtab %} {% endtabs %}

Трэйты, классы и методы определяются с помощью простого и легкого синтаксиса:

{% tabs scala-features-5 class=tabs-scala-version %} {% tab 'Scala 2' for=scala-features-5 %}

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("⎞⎜⎛  ⎞⎜⎛")
}

{% endtab %} {% tab 'Scala 3' for=scala-features-5 %}

trait Animal:
  def speak(): Unit

trait HasTail:
  def wagTail(): Unit

class Dog extends Animal, HasTail:
  def speak(): Unit = println("Woof")
  def wagTail(): Unit = println("⎞⎜⎛  ⎞⎜⎛")

{% endtab %} {% endtabs %}

Исследования показали, что время, которое разработчик тратит на чтение и написание кода, составляет как минимум 10:1, поэтому важно писать краткий и читабельный код.

Ощущение динамики

Scala — это язык со статической типизацией, но благодаря своим возможностям вывода типов он кажется динамичным. Все эти выражения выглядят как языки с динамической типизацией, такие как Python или Ruby, но это все Scala:

{% tabs scala-features-6 class=tabs-scala-version %} {% tab 'Scala 2' for=scala-features-6 %}

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)

{% endtab %} {% tab 'Scala 3' for=scala-features-6 %}

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)

{% endtab %} {% endtabs %}

Как утверждает Heather Miller, Scala считается сильным языком со статической типизацией, и вы получаете все преимущества статических типов:

  • Корректность: вы обнаруживаете большинство ошибок во время компиляции
  • Отличная поддержка IDE
    • Надежное автодополнение кода
    • Отлов ошибок во время компиляции означает отлов ошибок по мере написания
    • Простой и надежный рефакторинг
  • Вы можете уверенно рефакторить свой код
  • Объявления типов методов сообщают читателям, что делает метод, и помогают служить документацией
  • Масштабируемость и удобство обслуживания: типы помогают обеспечить корректность в произвольно больших приложениях и командах разработчиков
  • Строгая типизация в сочетании с превосходным выводом типов позволяет использовать такие механизмы, как [контекстная абстракция]({{ site.scala3ref }}/contextual), которая позволяет вам опускать шаблонный код. Часто этот шаблонный код может быть выведен компилятором на основе определений типов и заданного контекста.

Выразительная система типов

Система типов в Scala во время компиляции обеспечивает безопасное и согласованное использование абстракций. В частности, система типов поддерживает:

  • [Выводимые типы]({% link _overviews/scala3-book/types-inferred.md %})
  • [Generic классы]({% link _overviews/scala3-book/types-generics.md %})
  • [Аннотации вариантности]({% link _overviews/scala3-book/types-variance.md %})
  • Верхняя и нижняя границы типов
  • Полиморфные методы
  • [Типы пересечения]({% link _overviews/scala3-book/types-intersection.md %})
  • [Типы объединения]({% link _overviews/scala3-book/types-union.md %})
  • [Лямбда-типы]({{ site.scala3ref }}/new-types/type-lambdas.html)
  • [Экземпляры given и предложения using]({% link _overviews/scala3-book/ca-context-parameters.md %})
  • [Методы расширения]({% link _overviews/scala3-book/ca-extension-methods.md %})
  • [Типовые классы]({% link _overviews/scala3-book/ca-type-classes.md %})
  • [Многостороннее равенство]({% link _overviews/scala3-book/ca-multiversal-equality.md %})
  • [Псевдонимы непрозрачного типа]({% link _overviews/scala3-book/types-opaque-types.md %})
  • [Открытые классы]({{ site.scala3ref }}/other-new-features/open-classes.html)
  • [Типы соответствия]({{ site.scala3ref }}/new-types/match-types.html)
  • [Зависимые типы функций]({{ site.scala3ref }}/new-types/dependent-function-types.html)
  • [Полиморфные функциональные типы]({{ site.scala3ref }}/new-types/polymorphic-function-types.html)
  • [Контекстные границы]({{ site.scala3ref }}/contextual/context-bounds.html)
  • [Контекстные функции]({{ site.scala3ref }}/contextual/context-functions.html)
  • Внутренние классы и элементы абстрактного типа как элементы объекта

В сочетании эти функции обеспечивают мощную основу для безопасного повторного использования программных абстракций и для безопасного расширения программного обеспечения.

Язык функционального программирования

Scala — это язык функционального программирования (ФП), что означает:

  • Функции — это значения, и их можно передавать, как и любое другое значение
  • Напрямую поддерживаются функции высшего порядка
  • Встроенные лямбда
  • Все в Scala — это выражение, возвращающее значение
  • Синтаксически легко использовать неизменяемые переменные, и их использование приветствуется
  • В стандартной библиотеке языка содержится множество неизменяемых классов коллекций
  • Эти классы коллекций поставляются с десятками функциональных методов: они не изменяют коллекцию, вместо этого возвращая обновленную копию данных

Объектно-ориентированный язык

Scala — это язык объектно-ориентированного программирования (ООП). Каждое значение — это экземпляр класса, а каждый “оператор” — это метод.

В Scala все типы наследуются от класса верхнего уровня Any, чьими непосредственными дочерними элементами являются AnyVal (типы значений, такие как Int и Boolean) и AnyRef (ссылочные типы, как в Java). Это означает, что различие в Java между примитивными и упакованными типами (например, int против Integer) отсутствует в Scala. Упаковка и распаковка полностью прозрачны для пользователя.

Поддерживает слияние ФП/ООП

Суть Scala заключается в слиянии функционального программирования и объектно-ориентированного программирования в типизированной среде:

  • Функции для логики
  • Объекты для модульности

Как заявил Мартин Одерски, “Scala был разработан, чтобы показать, что слияние функционального и объектно-ориентированного программирования возможно и практично”.

Вывод терминов стал более понятным

После Haskell Scala был вторым популярным языком, в котором была некоторая форма неявных (implicits) выражений. В Scala 3 эти концепции были полностью переосмыслены и реализованы более четко.

Основная идея заключается в выводе терминов: на основе заданного, компилятор синтезирует “канонический” термин, который имеет этот тип. В Scala параметр контекста напрямую ведет к выводимому термину аргумента, который также может быть записан явно.

Примеры использования этой концепции включают реализацию [типовых классов]({% link _overviews/scala3-book/ca-type-classes.md %}), установление контекста, внедрение зависимостей, выражение возможностей, вычисление новых типов и доказательство отношений между ними.

Scala 3 делает этот процесс более понятным, чем когда-либо прежде. О контекстных абстракциях можно прочесть в [Справочной документации]({{ site.scala3ref }}/contextual).

Клиент & сервер

Код 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:

{% tabs scala-features-7 %} {% tab 'Scala 2 и 3' for=scala-features-7 %}

import java.io.*
val br = BufferedReader(FileReader(filename))
// чтение файла в `br` ...

{% endtab %} {% endtabs %}

Использование Java-кода в Scala, как правило, не вызывает затруднений.

В Scala также можно использовать коллекции Java, и если вы хотите использовать с ними богатый набор методов классов коллекций Scala, то можете преобразовать их с помощью всего нескольких строк кода:

{% tabs scala-features-8 %} {% tab 'Scala 2 и 3' for=scala-features-8 %}

import scala.jdk.CollectionConverters.*
val scalaList: Seq[Integer] = JavaClass.getJavaList().asScala.toSeq

{% endtab %} {% endtabs %}

Богатство библиотек

Как будет видно в третьем разделе этой страницы, библиотеки и фреймворки Scala, подобные нижеследующим, были написаны для поддержки загруженных веб-сайтов и работы с огромными наборами данных:

  1. Play Framework — это легкая, без сохранения состояния, удобная для web, удобная для разработчиков архитектура для создания масштабируемых приложений
  2. 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][givens])
    • [Методы расширения][extension] заменяют неявные классы более понятным и простым механизмом
    • Добавление модификатора open для классов заставляет разработчика намеренно объявить, что класс открыт для модификации, тем самым ограничивая специальные расширения кодовой базы
    • [Многостороннее равенство][multiversal] исключает бессмысленные сравнения с == и != (т.е. попытки сравнить Person с Planet)
    • Гораздо проще реализуются макросы
    • Объединение и пересечение предлагают гибкий способ моделирования типов
    • Параметры трейтов заменяют и упрощают ранние инициализаторы
    • [Псевдонимы непрозрачных типов][opaque_types] заменяют большинство случаев использования классов значений, гарантируя при этом отсутствие упаковки
    • Export предложения обеспечивают простой и общий способ выражения агрегации, который может заменить предыдущий шаблон фасада объектов пакета, наследуемых от классов
    • Синтаксис procedure был удален, а синтаксис varargs - изменен, чтобы сделать язык более согласованным
    • @infix аннотация делает очевидным желаемое применение метода
    • Аннотация метода [@targetName]({{ site.scala3ref }}/other-new-features/targetName.html) определяет альтернативное имя метода, улучшая совместимость с Java и позволяя указывать псевдонимы для символических операторов

Демонстрация всех этих функций заняла бы слишком много места, но перейдите по ссылкам в пунктах выше, чтобы увидеть эти функции в действии. Все эти функции подробно обсуждаются на страницах New, Changed и Dropped функций в [обзорной документации][reference].

Экосистема 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 обладает множеством замечательных функций высокоуровневого языка программирования, низкоуровневого языка программирования и богатой экосистемой разработчиков.

[reference]: {{ site.scala3ref }}/overview.html [multiversal]: {% link _overviews/scala3-book/ca-multiversal-equality.md %} [extension]: {% link _overviews/scala3-book/ca-extension-methods.md %} [givens]: {% link _overviews/scala3-book/ca-context-parameters.md %} [opaque_types]: {% link _overviews/scala3-book/types-opaque-types.md %}