Главная страница

Scala. Профессиональное программирование 2022. Одерски Мартин, Спун Лекс, Веннерс Билл, Соммерс ФрэнкО41 Scala. Профессиональное программирование. 5е изд спб. Питер, 2022. 608 с. ил. Серия Библиотека программиста


Скачать 6.24 Mb.
НазваниеОдерски Мартин, Спун Лекс, Веннерс Билл, Соммерс ФрэнкО41 Scala. Профессиональное программирование. 5е изд спб. Питер, 2022. 608 с. ил. Серия Библиотека программиста
Дата27.04.2023
Размер6.24 Mb.
Формат файлаpdf
Имя файлаScala. Профессиональное программирование 2022.pdf
ТипДокументы
#1094967
страница7 из 64
1   2   3   4   5   6   7   8   9   10   ...   64
53
Вывод типа в Scala может заходить довольно далеко. Фактически пользова­
тельский код нередко вообще обходится без явного задания типов. Поэтому программы на Scala часто выглядят похожими на программы, написанные на динамически типизированных языках скриптов. Это, в частности, спра­
ведливо для прикладного клиентского кода, который склеивается из заранее написанных библиотечных компонентов. Но для них самих это менее харак­
терно, поскольку в них зачастую применяются довольно сложные типы, не допускающие гибкого использования таких схем. И это вполне естественно.
Ведь сигнатуры типов элементов, составляющих интерфейс повторно исполь­
зуемых компонентов, должны задаваться в явном виде, поскольку составляют существенную часть соглашения между компонентом и его клиентами.
1 .4 . Истоки Scala
На идею создания Scala повлияли многие языки программирования и идеи, выработанные на основе исследований таких языков. Фактически обновле­
ния в Scala незначительны — большинство характерных особенностей языка уже применялось в том или ином виде в других языках программирования.
Инновации в Scala появляются в основном из того, как его конструкции сводятся воедино. В этом разделе будут перечислены основные факторы, оказавшие влияние на структуру языка Scala. Перечень не может быть ис­
черпывающим, поскольку в дизайне языков программирования так много толковых идей, что перечислить здесь их все просто невозможно.
На внешнем уровне Scala позаимствовал существенную часть синтаксиса у Java и C#, которые, в свою очередь, взяли большинство своих синтаксиче­
ских соглашений у C и C++. Выражения, инструкции и блоки — в основном из Java, как, собственно, и синтаксис классов, создание пакетов и импорт
1 1
Главное отличие от Java касается синтаксиса для объявления типов: вместо «Тип переменная», как в Java, задействуется форма «переменная: Тип». Используемый в Scala постфиксный синтаксис типа похож на синтаксис, применяемый в Pascal,
Modula­2 или Eiffel. Основная причина такого отклонения имеет отношение к ло­
гическому выводу типов, зачастую позволяющему опускать тип переменной или тип возвращаемого методом значения. Легче использовать синтаксис «переменная:
Тип», поскольку двоеточие и тип можно просто не указывать. Но в стиле языка C, применяющем форму «Тип переменная», просто так не указывать тип нельзя, поскольку при этом исчезнет сам признак начала определения. Неуказанный тип в качестве заполнителя требует какое­нибудь ключевое слово (C# 3.0, в котором имеется логический вывод типов, для этой цели задействует ключевое слово var).
Такое альтернативное ключевое слово представляется несколько более надуман­
ным и менее привычным, чем подход, который используется в Scala.

54 Глава 1 • Масштабируемый язык
Кроме синтаксиса, Scala позаимствовал и другие элементы Java, такие как его основные типы, библиотеки классов и модель выполнения.
Scala многим обязан и другим языкам. Его однородная модель объектов впервые появилась в Smalltalk и впоследствии была принята языком Ruby.
Его идея универсальной вложенности (почти каждую конструкцию в Scala можно вложить в любую другую) реализована также в Algol, Simula, а в по­
следнее время в Beta и gbeta. Его принцип единообразного доступа к вызову методов и выбору полей пришел из Eiffel. Его подход к функциональному программированию очень близок по духу к применяемому в семействе языков ML, видными представителями которого являются SML, OCaml и F#. Многие функции высшего порядка в стандартной библиотеке Scala присутствуют также в ML или Haskell. Толчком для появления в Scala не­
явных параметров стали классы типов языка Haskell — в более классическом объектно­ориентированном окружении они дают аналогичные результаты.
Используемая в Scala основная библиотека многопоточного вычисления на основе акторов — Akka — создавалась под сильным влиянием особенностей языка Erlang.
Scala не первый язык, делающий упор на масштабируемость и расширяе­
мость. Такое понятие, как расширяемые языки, которые могут охватывать различные области применения, впервые встречается в статье Питера Лэнди­
на (Peter Landin) 1966 года (язык, описанный в этой статье, — Iswim — стоит рядом с Lisp как один из первых функциональных языков) [Lan66]. Конкрет­
ная идея рассматривать инфиксный оператор как функцию восходит к Iswim и Smalltalk. Другая важная идея — разрешить функциональный литерал
(или блок) в качестве параметра, который позволяет библиотекам опреде­
лять управляющие структуры. Опять же это восходит к Iswim и Smalltalk.
И Smalltalk, и Lisp обладают гибким синтаксисом, который широко приме­
нялся для создания внутренних специфичных для конкретной предметной области языков. C++ — еще один масштабируемый язык, который можно адаптировать и расширить с помощью перегрузки операторов и его систе­
мы шаблонов; по сравнению со Scala он построен на более низкоуровневом, более системно — ориентированном ядре.
Кроме того, Scala не первый язык, объединяющий в себе функциональное и объектно­ориентированное программирование, хотя, вероятно, в этом направлении продвинулся гораздо дальше прочих. К числу других языков, объединивших некоторые элементы функционального программирования с объектно­ориентированным, относятся Ruby, Smalltalk и Python. Расши­
рения Java­подобного ядра некоторыми функциональными идеями были предприняты на Java­платформе в Pizza, Nice, Multi­Java и самом Java 8.

Резюме 55
Существуют также изначально функциональные языки, которые приобрели систему объектов. В качестве примера можно привести OCaml, F# и PLT­
Scheme.
В Scala применяются также некоторые нововведения в области языков программирования. Например, его абстрактные типы — более объектно­
ориентированная альтернатива обобщенным типам, его трейты позволяют выполнять гибкую сборку компонентов, а экстракторы обеспечивают неза­
висимый от представления способ сопоставления с образцом. Эти нововведе­
ния были озвучены в статьях на конференциях по языкам программирования в последние годы
1
Резюме
Ознакомившись с текущей главой, вы получили некоторое представление о том, что такое Scala и как он может помочь программисту в работе. Разу­
меется, этот язык не решит все ваши проблемы и не увеличит волшебным образом вашу личную продуктивность. Следует заранее предупредить, что
Scala нужно применять искусно, а для этого потребуется получить некото­
рые знания и практические навыки. Если вы перешли к Scala от языка Java, то одними из наиболее сложных аспектов его изучения для вас могут стать система типов Scala, которая существенно богаче, чем у Java, и его поддерж­
ка функционального стиля программирования. Цель данной книги — по­
служить руководством при поэтапном, от простого к сложному, изучении особенностей Scala. Полагаем, что вы приобретете весьма полезный интел­
лектуальный опыт, расширяющий ваш кругозор и изменяющий взгляд на проектирование программных средств. Надеемся, что вдобавок вы получите от программирования на Scala истинное удовольствие и познаете творческое вдохновение.
В следующей главе вы приступите к написанию кода Scala.
1
Для получения дополнительной информации см. [Ode03], [Ode05] и [Emi07] в би­
блиографии.

2
Первые шаги в Scala
Пришло время написать какой­нибудь код на Scala. Прежде чем углубиться в руководство по этому языку, мы приведем две обзорные главы по нему и, что наиболее важно, заставим вас приступить к написанию кода. Реко­
мендуем по мере освоения материала на практике проверить работу всех примеров кода, представленных в этой и последующей главах. Лучше всего приступить к изучению Scala, программируя на данном языке.
Запуск представленных далее примеров возможен с помощью стандартной установки Scala. Чтобы ее осуществить, перейдите по адресу www .scala-lang .
org/downloads и следуйте инструкциям для вашей платформы. На этой страни­
це описано несколько способов установки Scala. Будем считать, что вы уже установили двоичные файлы Scala и добавили их в переменную окружения path
1
, что необходимо для выполнения шагов из этой главы.
Если вы опытный программист, но новичок в Scala, то внимательно про­
читайте следующие две главы: в них приводится достаточный объем ин­
формации, позволяющий приступить к написанию полезных программ на этом языке. Если же опыт программирования у вас невелик, то часть материалов может показаться чем­то загадочным. Однако не стоит пере­
живать. Чтобы ускорить процесс изучения, нам пришлось обойтись без некоторых подробностей. Более обстоятельные пояснения мы представим в последующих главах. Кроме того, в следующих двух главах дадим ряд сносок с указанием разделов книги, в которых можно найти более по­
дробные объяснения.
1
Мы протестировали примеры из этой книги со Scala версии 3.0.0.

Шаг 1 . Осваиваем Scala REPL 57
Шаг 1 . Осваиваем Scala REPL
Самый простой способ начать работу со Scala — использовать Scala REPL
1
, интерактивную оболочку для написания выражений и программ Scala. REPL, который называется scala
, оценивает введенные вами выражения и выво­
дит полученное значение. Чтобы его использовать, нужно набрать scala в командной строке
2
:
$ scala
Starting Scala REPL...
scala>
После того как вы наберете выражение, например
1
+
2
, и нажмете клавишу
Enter
:
scala> 1 + 2
REPL выведет на экран:
val res0: Int = 3
Эта строка включает:
z z
ключевое слово val
, объявляющее переменную;
z z
автоматически сгенерированное или определенное пользователем имя для ссылки на вычисленное значение (
res0
, означающее результат 0);
z z
двоеточие (
:
), за которым следует тип выражения (
Int
);
z z
знак равенства (
=
);
z z
значение, полученное в результате вычисления выражения (
3
).
Тип
Int означает класс
Int в пакете scala
. Пакеты в Scala аналогичны пакетам в Java — они разбивают глобальное пространство имен на части и предоставляют механизм для сокрытия данных
3
. Значения класса
Int соответствуют int
­значениям в Java. Если говорить в общем, то все прими­
тивные типы Java имеют соответствующие классы в пакете scala
. Например, sca la.Boolean соответствует Java­типу boolean
. А scala.Float соответствует
1
REPL означает read, evaluate, print, loop («чтение, оценка, печать, цикл»).
2
Если вы используете Windows, вам нужно будет ввести команду scala в оболочке командной строки.
3
Если вы не знакомы с пакетами Java, то их можно рассматривать как средство предоставления классам полных имен.
Int входит в пакет scala
Int
— простое имя класса, а scala.Int
— полное. Подробнее о пакетах рассказывается в главе 12.

58 Глава 2 • Первые шаги в Scala
Java­типу float
. И при компиляции вашего кода Scala в байт­код Java ком­
пилятор Scala будет по возможности использовать примитивные типы Java, чтобы обеспечить вам преимущество в производительности при работе с примитивными типами.
Идентификатор resX
может использоваться в последующих строках. Напри­
мер, поскольку ранее для res0
было установлено значение
3
, то результат выражения res0
*
3
будет равен
9
:
scala> res0 * 3
val res1: Int = 9
Чтобы вывести на экран необходимое, но недостаточно информативное при­
ветствие
Hello,
world!
, наберите следующую команду:
scala> println("Hello, world!")
Hello, world!
Функция println выводит на стандартное устройство вывода переданную ей строку, подобно тому как это делает
System.out.println в Java.
Шаг 2 . Объявляем переменные
В Scala имеются две разновидности переменных: val
­переменные и var
­
переменные. Первые аналогичны финальным переменным в Java. После инициализации val
­переменная уже никогда не может быть присвоена повторно. В отличие от нее var
­переменная аналогична нефинальной пере­
менной в Java и может быть присвоена повторно в течение своего жизненного цикла. Определение val
­переменной выглядит так:
scala> val msg = "Hello, world!"
val msg: String = Hello, world!
Эта инструкция вводит в употребление переменную msg в качестве имени для строки "Hello,
world!"
. Типом msg является java.lang.String
, поскольку строки в JVM Scala реализуются Java­классом
String
Если вы привыкли объявлять переменные в Java, то в этом примере кода можете заметить одно существенное отличие: в val
­определении нигде не фигурируют ни java.lang.String
, ни
String
. Пример демонстрирует ло-
гический вывод типов, то есть возможность Scala определять неуказанные типы. В данном случае, поскольку вы инициализировали msg строковым литералом, Scala придет к выводу, что типом msg должен быть
String
. Когда

Шаг 2 . Объявляем переменные 59
REPL (или компилятор) Scala хочет выполнить вывод типов, зачастую лучше всего будет позволить ему сделать это, не засоряя код ненужными явными аннотациями типов. Но при желании можете указать тип явно, и, вероятно, иногда это придется делать. Явная аннотация типа может не только гарантировать, что компилятор Scala выведет желаемый тип, но и послужить полезной документацией для тех, кто впоследствии станет читать ваш код. В отличие от Java, где тип переменной указывается перед ее именем, в Scala вы указываете тип переменной после ее имени, отделяя его двоеточием, например:
scala> val msg2: java.lang.String = "Hello again, world!"
val msg2: String = Hello again, world!
Или же, поскольку типы java.lang вполне опознаваемы в программах на
Scala по их простым именам
1
, запись можно упростить:
scala> val msg3: String = "Hello yet again, world!"
msg3: String = Hello yet again, world!
Возвратимся к исходной переменной msg
. Поскольку она определена, то ею можно воспользоваться в соответствии с вашими ожиданиями, например:
scala> println(msg)
Hello, world!
Учитывая, что msg является val
­, а не var
­переменной, вы не сможете по­
вторно присвоить ей другое значение
2
. Посмотрите, к примеру, как REPL выражает свое недовольство при попытке сделать следующее:
scala> msg = "Goodbye cruel world!"
1 |msg = "Goodbye cruel world!"
|ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
|Reassignment to val msg
Если необходимо выполнить повторное присваивание, следует воспользо­
ваться var
­переменной:
scala> var greeting = "Hello, world!"
var greeting: String = Hello, world!
Как только приветствие станет var
­, а не val
­переменной, ему можно будет присвоить другое значение. Если, к примеру, чуть позже вы станете более
1
Простым именем java.lang.String является
String
2
Но в интерпретаторе новую val
­переменную можно определить с именем, которое до этого уже использовалось. Этот механизм рассматривается в разделе 7.7.

60 Глава 2 • Первые шаги в Scala раздражительными, то можете поменять приветствие на просьбу оставить вас в покое:
scala> greeting = "Leave me alone, world!"
greeting: String = Leave me alone, world!
Чтобы ввести в REPL код, который не помещается в одну строку, просто про­
должайте набирать код после заполнения первой строки. Если набор кода еще не завершен, то REPL отреагирует установкой на следующей строке вертикальной черты:
scala> val multiLine =
| "This is the next line."
multiLine: String = This is the next line.
Если вы понимаете, что набрали что­то не так, но REPL все еще ожидает ввода дополнительных данных, вы можете использовать клавиши со стрелками для перемещения вверх, вниз, влево или вправо, чтобы исправить ошибки. Если вы хотите полностью отменить ввод, вы можете выйти, дважды нажав
Enter
:
scala> val oops =
|
|
You typed two blank lines. Starting a new command.
scala>
Далее по тексту мы чаще всего будем опускать подсказку scala
, верикальные линии и вывод REPL при успешном вводе, чтобы упростить чтение кода
(и облегчить копирование и вставку из электронной книги PDF в REPL).
Шаг 3 . Определяем функции
После работы с переменными в Scala вам, вероятно, захотелось написать какие­нибудь функции. Это делается так:
def max(x: Int, y: Int): Int =
if x > y then x else y
Определение функции начинается с ключевого слова def
. После имени функ­
ции, в данном случае max
, стоит заключенный в круглые скобки перечень параметров, разделенных запятыми. За каждым параметром функции должна следовать аннотация типа, перед которой ставится двоеточие, поскольку ком­
пилятор Scala (и REPL, но с этого момента будет упоминаться только компи­

Шаг 3 . Определяем функции 61
лятор) не выводит типы параметров функции. В данном примере функция по имени max получает два параметра, x
и y
, и оба они относятся к типу
Int
После закрывающей круглой скобки перечня параметров функции max обна­
руживается аннотация типа
:
Int
. Она определяет результирующий тип самой функции max
1
. За типом результата функции следует знак равенства и тело функции, которое отделено отступами. В этом случае тело содержит одно выражение if
, которое в качестве результата функции max выбирает либо x
, либо y
, в зависимости от того, что больше. Как показано здесь, выражение if в Scala может приводить к значению, аналогичному тернарному оператору
Java. Например, в Scala выражение if x
>
y then x
else y
вычисляется точно так же, как выражение
(x
>
y)
?
x
:
y в Java. Знак равенства, предшествующий телу функции, дает понять, что с точки зрения функционального мира функ­
ция определяет выражение, результатом вычисления которого становится значение. Основная структура функции показана на рис. 2.1.
Рис. 2.1. Основная форма определения функции в Scala
Иногда компилятор Scala может потребовать от вас указать результиру­
ющий тип функции. Если, к примеру, функция является рекурсивной
2
, то вы должны указать ее результирующий тип явно. Но в случае с функцией max вы можете не указывать результирующий тип функции — компилятор выведет его самостоятельно
3
. Кроме того, если функция состоит всего лишь
1
В Java тип возвращаемого из метода значения является возвращаемым типом.
В Scala то же самое понятие называется результирующим типом.
2
Функция называется рекурсивной, если вызывает саму себя.
3
Тем не менее зачастую есть смысл указывать результирующий тип явно, даже когда компилятор этого не требует. Такая аннотация типа может упростить чтение кода, поскольку читателю не придется изучать тело функции, чтобы определить, каким будет вывод результирующего типа.

1   2   3   4   5   6   7   8   9   10   ...   64


написать администратору сайта