Язык программирования Scala тонкости и основные моменты
В этой статье мы рассмотрим язык программирования Scala. Что это за язык программирования Scala тонкости и основные моменты. Просто чтобы у вас сложилось понимание зачем его стоит изучать.
Если вы новичок в программировании Scala или заглянули сюда из других областей программирования, таких как Java или .NET, не беспокойтесь о внутренних тонкостях Scala. на самом деле если вникнуть — все окажется достаточно простым, в той или иной степени. Кстати вот неплохой курс для новичков: https://itvdn.com/ru/video/scala-starter. Из него вы поймете основы языка Scala — и у него есть продолжение!
Scala
1. Так что же такое язык программирования Scala?
Scala является функциональным и объектно-ориентированным языком программирования, который работает на JVM ! Это было не так сложно 🙂
Возможно, картинка ниже будет хорошей визуальной иллюстрацией.
2. Кто создал язык программирования scala?
Язык был разработан Мартином Одерским, и язык работает на виртуальной машине Java (JVM). Язык существует уже около десяти лет и активно развивается. На момент написания статьи в репозитории GitHub Scala было около 500 участников.
Курс Scala 2022. Лекция 1, часть 1. Введение в Scala
Примечание:
- GitHub — это популярный репозиторий, в котором вы можете просматривать и участвовать в самых разных проектах с открытым исходным кодом.
- GitHub не следует путать с контролем версий Git. Согласно википедии, GitHub — это хостинг Git-репозитория, а Git — это контроль версий, похожий на SVN, CVS или Perforce.
3. Что такое JDK / JVM?
Если вы новичок в программировании, вы можете спросить себя, что такое JDK ? Короче говоря, JDK содержит набор библиотек и утилит для разработки приложений Java. JDK также поставляется с средой Java Runtime Environment, также известной как JRE. Кроме того, вы также услышите термин Java Virtual Machine или JVM, используемый для обозначения JRE.
Если вы заглянули сюда из другого языка программирования, например, .NET, тогда вы можете думать, что JDK похож на NET Runtime в том смысле, что предоставляет среду для написания и запуска вашего кода.
Если вы занимались разработкой на Java, то я уверен, что вы можете пропустить это объяснение и перейти к следующему 🙂
4. Зачем JDK для написания Scala-приложений?
Если вы помните из шага 1, Scala — это и язык функционального, и объектно-ориентированного программирования, который работает на JVM!
В результате он прекрасно взаимодействует с существующим кодом Java и библиотеками.
Пара моментов:
- На первом этапе вы, как разработчик, напишите свой код Scala. Однако, скажем, например, что компания, в которой вы работаете, уже имеет существующие библиотеки Java, которые, возможно, потребовали бы слишком много усилий и ресурсов для рефакторинга в Scala. Кроме того, вы можете использовать сторонние библиотеки Java, которые вы, очевидно, не можете изменить или используете библиотеки Java с открытым исходным кодом. К счастью, переход на Scala и использование его функциональных возможностей не означает, что вам нужно начинать с нуля. Он работает с вашим существующим стеком кода Java!
- На этапе 2 вам придется вызывать компилятор Java, а именно javac для компиляции вашего кода Java. Что касается компиляции кода Scala, вы должны использовать компилятор Scala, а именно scalac. В большинстве случаев вы будете использовать интегрированную среду разработки или IDE, чтобы помочь вам легко писать и компилировать ваши приложения.
- На третьем этапе ваши скомпилированные классы преобразуются в байт-код, который по сути является языком, который понимает JVM и откуда в конечном итоге будет выполняться ваш код. Если вы новичок в среде Java или в программировании в целом, вы можете прочитать руководство из Oracle, в котором содержится некоторая дополнительная информация о том, как классы Java компилируются и в конечном итоге выполняются в JVM.
На этом мы завершаем нашу статью о том, что такое язык программирования Scala, и я надеюсь, что вы нашли ее полезной !
1. Обзор языка Scala 👍 (Уроки программирования на Scala)
Резюме
В этой статье мы рассмотрели следующее:
- Что такое Скала
- Кто создал Scala
- Что такое JDK / JVM
- Почему нам нужна JVM для запуска приложений Scala
Совет
За дополнительной информацией о Scala вы можете обратиться на itvdn.com. Там есть все!
Источник: www.doctorrouter.ru
Scala. Введение
Не так давно я заинтересовался одним из многочисленных ныне языков под JVM — Scala. Причин тому много, основная — всё нарастающее со временем чувство неудобства при работе с cpp-подобными языками. Взгляд мой попеременно падал на Ruby, Groovy, Python, но все они оставляли впечатление инструментов, не совсем подходящих для моего обычного круга рабочих задач (Python-таки хорош, но у нетипизированных языков есть свои ограничения). Scala же, напротив, показалась вполне годным языком. Так как поиск по хабру никаких статей о ней не выловил (было несколько, но мягко говоря не вводных), я решил написать маленький обзор и поделиться им с массами.
Немного философии языка в вольном изложении
Какие основные цели преследовали создатели языка? Согласно моим мироощущениям они такие:
Во-первых, совместимость. Среди своих задач разработчики ставили поддержание совместимости с Java-языком и тысячами примеров говнокода разработок на ней для решения самых разнообразных задач.
Во-вторых, интенсивное насыщение языка функциональными фичами, которые, в основном, (но далеко не полностью) составляют его отличия от Java.
В-третьих, облегчение нашего с вами труда. Действительно, компилятор Scala понимает программиста с полуслова, писать код специально, чтобы втолковывать ему, что я не верблюд, мне не довелось пока.
В-четвёртых, поддержка и стимулирование написания модульных, слабосвязанных программных компонентов в сочетании с широкими возможностями адаптации уже существующих. Цели не то, чтобы совсем противоположные, но порождающие известные трудности для одновременного достижения. Что ж, посмотрим что получится.
В-пятых, это поддержка параллелизма. К сожалению у меня руки и голова до этой области не дошли (надеюсь пока), но акцент на этом моменте делается постоянно на всех ресурсах по языку.
Для экспериментов с языком достаточно поставить соответствующий плагин на любимую IDE отсюда.
Итак, давайте посмотрим на сам язык…
Общие идеи языка, примеры синтаксиса
Самое, пожалуй, важное, — это «унифицированная модель объектов». Этот термин расшифровывается авторами так: «каждое значение — объект, каждая операция — вызов метода». Это, конечно, не «всё — объект», но сущностей в сравнении с Java убыло, а чем меньше сущностей — тем легче жизнь 🙂 В прикладном плане это означает, что числа и символы сделались неизменяемым объектами, обитающими в общей куче, все операции приобретают ссылочную семантику. Например, код 5 + 5 вполне валиден, и породит новый объект в куче, который оперативненько оприходует сборщик мусора (на самом деле, я тихо надеюсь, что компилятор поймёт глубину замысла и порождать он ничего не будет 🙂 ).
После столь возвышенного введения можно глянуть на решение классической задачи:
object Main def main(args:Array[ String ]) :Unit = print( «Hello, » + args(0) + «!» )
>
>
- Можно объявлять отдельные объекты. Ничего необычного в этом нет, подобная возможность имеется, например в Groovy. Ведут себя такие объекты так же как написанные на Java реализации шаблона Singelton.
- Объявление фукции выглядит непривычно, но вполне читабельно: [ключевое слово def] [имя]([список параметров]):[возвращаемый тип] = [блок кода].
- В качестве типа, не несущего информационной нагрузки, выступает тип Unit. Он вполне аналогичен void в C-подобных языках.
- Объявление параметра функции (а на самом деле и локальной переменной тоже) выглядит как [имя]:[тип].
- Для параметризации типа используется не привычные нам <> , а казалось бы, навсегда закреплённые за масивами [] .
- Для обращения к элементам массива(экое непотребство) используются () .
- Имеется какие-то встроенные функции, доступные в коде по умолчанию без всяких дополнительных импортов.
println( ( «Hello, » + args(0) + «!» ).toUpperCase )
println( «Hello, » + args(0) + «!» toUpperCase )
Как из него следует, использование оператора . совершенно не необходимо. Синтаксис языка вполне допускает использование вместо него пробела (также аргументы метода можно писать без скобок и запятых сразу после имени метода). И как мы видим, это оказывается вполне полезно: в первой строке высокоприоритетный оператор . заставляет нас писать ненужные, засоряющие код скобки, во второй получается более краткая и наглядная форма записи.
В качестве подспорья разработчику Scala поддерживает также интерактивный режим. То есть, можно запустить интерпретатор и по одной вводить комманды. Интерпретатор, встроенный в IDE, как-то нерегулярно работает, его отдельный вариант есть в репозитариях Убунты, думаю у остальных дистрибутивов тоже всё хорошо, счастливым обладателям Windows как всегда придётся помучаться 🙂 Интерпретатор запускается самым необычным способом:
$ scala
Welcome to Scala version 2.7.3final (Java HotSpot(TM) Server VM, Java 1.6.0_16).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
Совсем маленький пример:
scala> 1 to 10
res0: Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Тут мы видим пример вызова метода с параметром. Если кто не догадался, у объекта класса Int 1 вызывается метод to с параметром того же типа 10, результат — диапазон значений.
Попробуем-ка мы теперь написать ещё одну функцию. Пусть она нам считает сумму чисел в заданном диапазоне, итак:
scala> def sum(a: Int, b: Int): Int = | var result = 0
| for (i | result
| >
sum: (Int,Int)Int
- При помощи ключевого слова var мы можем объявлять локальные переменные
- Результатом вычисления блока является последнее выражение в нём
- В нашем распоряжении имеется цикл for, который может выполнять вычисления для значений в заданном диапазоне (на самом деле для объектов в любом объекте — контейнере)
Операции над функциями
Что же мы такого можем с ними тут делать? Да что угодно =) Функции являются полноценными объектами программы. Их можно хранить как свойства объектов, передавать как параметры и возвращаемые значения и собственно создавать во время выполнения. Данные свойства позволяют строить так называемые функции высокого порядка, оперирующие себе подобными.
Для иллюстрации рассмотрим ставший классическим пример вычисления суммы:
scala> def sum(f: Int => Int, a: Int, b: Int): Int =
| if (a > b) 0 else f(a) + sum(f, a + 1, b) sum: ((Int) => Int,Int,Int)Int
В данном примере определяется функция sum, представляющая знакомый, надеюсь, всем оператор суммы. Параметры имеют следующий смысл:
f — функция преобразования целого числа из пределов суммирования в элемент суммы. Обратите внимание на объявление типа параметра: знак => означает, что параметр — функция, типы принимаемых значений перечисляются слева от него в круглых скобках (если параметр один, как в данном примере, их допустимо опустить), тип возвращаемого результата справа.
Работает она тривиально: вычисляет значение функции в нижней границе диапазона и складывает его с результатом вычисления себя самой в диапазоне на 1 меньшем.
Также в этом примере видна ещё одна особенность языка — if является выражением, имеющим значение (кстати, использованный ранее for — тоже выражение, его результат типа Unit ). Если условие истина, то его результат первый вариант, иначе — второй.
a и b — пределы суммирования.
Ещё пара функций id и square , они равны своему параметру и его квадрату соответственно.
scala> def id(x: Int): Int = x
id: (Int)Int
scala> def square(x: Int): Int = x * x
square: (Int)Int
Тут надо сделать ещё одно лирическое отступление: функции в Scala имеют декларативный стиль объявления. Они описывают не как получить результат, а чему он равен. Но если требуется организовать последовательные вычисления в теле функции, нет проблем — у нас есть блоки.
Теперь можно воспользоваться тем, что мы написали ранее.
scala> sum(id, 1, 5)
res1: Int = 15
scala> sum(square, 1, 5)
res2: Int = 55
Здесь происходит кульминация этой части — мы берём и передаём функцию в другую функцию. Никаких интерфейсов, анонимных классов, делегатов: вот оно — маленькое счастье.
Я здесь намеренно не стал приводить примеры вложенных и анонимных функций, карринга. Всё это Scala умеет, но всё нельзя включить в небольшой обзор. Думаю приведёный пример достаточен для понимания важности и, главное, — удобства функций высокого порядка как инструмента программирования. Напоследок могу посоветовать почитать эту главу замечательной книги по программированию.
Особенности классов
Давайте опишем несложный класс. Пусть это будет комлексное число. Создадим следующий код:
class Complex(r: Double, i: Double) def real = r
def image = i
def magnitude = Math .sqrt(r*r + i*i)
def angle = Math .atan2(i, r)
def + (that: Complex) = new Complex( this .real + that.real, this .image + that.image)
override def toString = real+ » + i*» +image+ » | » +magnitude+ «*e^(i*» +angle+ «))»
>
object Main def main(args:Array[ String ]) :Unit = val first = new Complex(1, 5)
val second = new Complex(2, 4)
val sum = first + second
println(first)
println(second)
println(sum)
>
>
Во-первых, клас объявлен с какими-то параметрами. Как несложно догадаться по продолжению, это параметры конструктора, которые доступны всё время жизни объекта.
Во-вторых, в классе объявлено несколько методов — селекторов. Одно семейство для декартового представления и одно для полярного. Как видим оба они используют параметры конструктора.
В-третьих, в классе объявлен оператор сложения. Объявлен он как обычный метод, принимает также Complex и возвращает его же.
Ну и наконец, для этого класса переопределена, без сомнения, знакомая всем Java-программистам функция toString . Важно отметить что на переопределение методов в Scala всегда необходимо явно указывать при помощи ключевого слова override .
- Занимает неоправданно много для своей функциональности места на экране
- Не умеет сравнивать себя с себе подобными
class Complex(val real: Double, val image: Double) extends Ordered[Complex] def magnitude = Math .sqrt(real*real + image*image)
def angle = Math .atan2(image, real)
def + (that: Complex) = new Complex( this .real + that.real, this .image + that.image)
def compare(that: Complex): Int = this .magnitude compare that.magnitude
override def toString = real+ » + i*» +image+ » | » +magnitude+ «*e^(i*» +angle+ «))»
>
object Main def main(args:Array[ String ]) :Unit = val first = new Complex(1, 5)
val second = new Complex(2, 4)
if (first > second )
println( «First greater» )
if (first < second )
println( «Second greater» )
if (first == second )
println( «They’re equal» )
>
>
- У параметров конструктора появилось ключевое слово val и исчезли соответствующие селекторы. Да, всё вполне очевидно, это разрешение компилятору создать селекторы для них автоматически.
- Добавилось наследование от незнакомого нам класса (а точнее trait’а) Ordered. Да не простого, а параметризованного нашим классом. Как следует из названия, он должен помочь нам с упорядочиванием наших экземпляров.
- Появился метод compare, который сравнивает два комплексных числа посредством сравнения их модулей.
- В тестовом методе появились использования операторов >,
class User private [ this ] var _name: String = «»
def name = _name toUpperCase
def name_=(name: String ) = _name = if (name != null ) name else «»
>
>
- Во-первых, этот класс использует уже знакомое нам ключевое слово var в своём теле, да не просто а с диковинным модификатором private[this] . Значение этого ключевого слова в теле класса абсолютно аналогично таковому в внутри блока(и даже, скажу по секрету, в конструктор его тоже можно запихать) и делает из имени после него изменяемый аттрибут класса. Диковинный модификатор заявляет, что переменная должна быть доступна только данному объекту. Можно было написать, например, private[User] и она стала бы доступна другим, нам подобным, объектам, или указать имя пакета (что-то это мне напоминает).
- Далее объявлена функция возвращающая наше поле в верхнем регистре.
- И в заключение, странная функция name_= , получающая строку в виде параметра, проверяющая что она не null и записывающая её в наше поле.
val user = new User( «Scala. » )
println(user.name)
user.name = «M. Odersky»
println(user.name)
Внимание, вывод: метод с именем _= вызывается при использовании конструкции . = . Насколько я знаю в Scala это второй хак (первый — преобразование () в вызов метода apply ), как Гвидо завещал c неявным преобразованием использования оператора в вызов метода.
Pattern-matching
- Создать функцию-конструктор с именем, совпадающим с класом.
- Имплементировать в классе toString, equals, hashCode на основе аргументов конструктора.
- Создать селекторы для всех аргументов конструктора.
case class KnownUser(val name: String ) extends User
case class AnonymousUser() extends User
object Test val users = List (KnownUser( «Mark» ), AnonymousUser(), KnownUser( «Phil» ))
def register(user: User): Unit = user match case KnownUser(name) => println( «User » + name + » registered» )
case AnonymousUser() => println( «Anonymous user can’t be registered» )
>
def main(args: Array[ String ]) =
users. foreach ( register )
>
Итак, общая картина кода: есть абстрактный класс пользователя, есть два его казуальных потомка: известный и анонимный пользователи. Мы хотим зарегистрировать некий список пользователей на (здесь включаем фантазию) встречу. Для чего и используем pattern-matching, который позволяет нам определить разное поведение метода для разных типов объектов и обеспечивает выборку данных из этих объектов.
- Конструкторы других case-классов. Тут всё вполне рекурсивно, глубина вложенности шаблона ограничивается безумием программиста не ограничивается.
- Переменные шаблона. Они становятся доступны в теле функции вычисления результата.
- Символы _ обозначающие любое, неинтересующее нас значение.
- Литералы языка. Например 1 или «Hello» .
Люди знакомые с базовыми принципами ООП конечно сразу заметят, что проблема эта вполне решается использованием виртуальных функций (более того предлагаемый подход является не лучшей практикой). Однако их использование несёт в себе две трудности: во-первых усложняет поддержку кода при большом числе таких функций (нам ведь захочется регистрировать пользователей и на события, и в группы, и в блоги и т.п., что для каждого случая создавать виртуальный метод?), во-вторых не решает проблемы с тем, что объекты одного типа могут принципиально иметь разную структуру и не иметь возможности предоставлять некоторые данные.
На вторую проблему хочется обратить особое внимание. Как выглядил бы приведённый выше код в Java? Один класс, если пользователь анонимный выставляем в имени null и проверяем каждый раз (эстеты вроде меня заводят методы типа isAnonymous , состоящие из сравнения поля с тем же null ). Проблемы налицо — неявно и небезопасно. Таких примеров великое множество, когда разные вариации структуры объектов объединяются в один класс, а неиспользуемые в конкретном случае забиваются null ‘ами, или того хуже придумывается значение по умолчанию. Scala позволяет явно описывать вариации структуры объектов, и предоставляет удобный механизм для работы с этими вариациями.
- У нас много функций. Да если у нас пара сотен операций, используемых по паре раз, зависящих от структуры и содержания объектов, система на основе case classes — pattern matching будет явно лучше поддерживаема.
- У нас мало классов. match из пары выриантов всегда хорошо читаем.
- У нас есть значительные вариации структуры объектов, которые однако надо хранить и обрабатывать единообразно.
Вывод типов
Думаю, вы уже заметили, что в коде я указывал типы только при объявлении классов и методов. В блоках кода я их практически всегда опускал. Дело в том, что если программист не указывает тип явно, Scala пытается определить его из контекста. Например при инициализации значения константы в определении def s = «Scala» компилятор определит тип константы как строку. Всё это также работает на обобщённых типах, например фрагмент выше val users = List(KnownUser(«Mark»), AnonymousUser(), KnownUser(«Phil»)) , создаёт константу типа List[User] , автоматически поднимаясь до подходящего уровня в иерархии наследования и используя его для параметризации типа-контейнера. На практике это означает, что можно значительно сэкономить на подобных объявлениях (для развлечения напишите делающий то же самое код на Java или C# 🙂 ).
Мда… К началу поста уже и скролить долго. Явно пора заканчивать. А сказать хотелось бы ещё про многое: про интереснейший механизм описания обобщённых классов, про неявные преобразования и то, что они на самом деле явные, ленивую инициализацию констант.
Мне и самому ещё только предстоит изучить модель многопоточности и своеобразный набор примитивов для её реаизации, разобраться с языковой поддержкой xml, поиграться с DSL-строением, посмотреть на их флагманский проект — Lift…
- Scala является весьма лаконичным и выразительным языком
- Она предоставляет мощный инструментарий для создания простых и красивых программ
UPD: поправил грамматику, спасибо всем оказавшим в этом помощь. Особенно ganqqwerty за массовые разборки с запятыми.
_________
All source code was highlighted with Source Code Highlighter .
Текст подготовлен в ХабраРедакторе
Источник: habr.com
Изучите Scala от 0 до 60: основы
Scala — это универсальный язык программирования высокого уровня, который предлагает баланс между разработкой функциональных и объектно-ориентированных программ.
Что такое функциональное программирование? Проще говоря, функции — это первоклассные граждане в функциональном программировании. Чтобы расширить основной набор функций программы, мы склонны писать дополнительные классы, расширяющие определенные руководящие принципы / интерфейсы. В функциональном программировании функции помогают нам добиться того же.