Эта серия статей предназначена для занятых программистов, которые хотят выучить Scala быстро, за 2 часа или меньше. Эти статьи представляют собой письменную версию мини-курса Rock the JVM Scala на Light Speed, которую вы можете найти бесплатно на YouTube или на веб-сайте Rock the JVM в виде видео.
Это третья статья серии, в которой речь пойдет о Scala как о функциональном языке программирования. Вы можете посмотреть его в виде видео или во встроенном видео ниже.
Итак, мы рассмотрели:
- Как начать работу с Scala
- Самые основы: значения, выражения, типы
- Ориентация на объект: классы, экземпляры, синглтоны, методы и базовые дженерики
- Функциональное программирование
- Сопоставление с образцом
Вам также может понравиться: Вам также может понравиться: Scala со скоростью света, Часть 1. Основы , Часть 2. Ориентация объекта , Часть 3. Функциональное программирование , Часть 4. Сопоставление с образцом
Ленивая оценка
На простом языке ленивое значение оценивается только при первом использовании.
Scala
xxxxxxxxxx
1
lazy val aLazyValue = 2
Ленивая оценка особенно полезна, когда для вычисления этого значения требуется много времени: вам не нужно останавливать весь код, вы будете делать это только тогда, когда вам нужно это значение. Просто чтобы доказать это, давайте вычислим значение с побочным эффектом, например, распечаткой чего-либо на консоль.
Scala
xxxxxxxxxx
1
lazy val lazyValueWithSideEffect = {
2
println("I am so very lazy!")
3
43
4
}
Если вы поместите это в отдельное приложение и запустите его без использования значения, ничего не появится — это потому, что значение не оценивается. Если вместо этого вы добавляете:
Scala
xxxxxxxxxx
1
val eagerValue = lazyValueWithSideEffect + 1
И снова запустите приложение, строка I am so very lazy!
внезапно снова появится в приложении. (пример наиболее заметен на видео)
Ленивые значения полезны при работе с бесконечными коллекциями, такими как Streams.
вариант
Думайте о Опционе как о «коллекции», содержащей не более одного элемента. Опции были созданы, чтобы избежать необходимости работать с пустыми значениями , что вызывает сильную головную боль в мире Java и большую защиту в производственном коде.
Scala
xxxxxxxxxx
1
def methodWhichCanReturnNull(): String = "hello, Scala"
2
val anOption = Option(methodWhichCanReturnNull()) // Some("hello, Scala")
3
// option = "collection" which contains at most one element: Some(value) or None
4
5
val stringProcessing = anOption match {
6
case Some(string) => s"I have obtained a valid string: $string"
7
case None => "I obtained nothing"
8
}
Если у вас есть небезопасный метод, который может вернуть ноль, вы можете сделать его безопасным, заключив вызов в Option. Опция будет одним из двух подтипов:
- Some (value), который является классом case
- Ни один, который является объектом
Мы можем очень легко сопоставить параметры, чтобы получить значения внутри. Что еще более важно, параметры работают так же, как списки, поэтому мы можем составить параметры с картой, flatMap, фильтром или для-понимания, как мы сделали со списками. Это функциональный стиль мышления, в котором мы составляем значения (в данном случае параметры), чтобы получить другие значения.
Пытаться
Try работает аналогично, так как вы можете думать о нем как о «коллекции», содержащей не более одного элемента. Try используется, когда код, который вы хотите оценить, может вызвать исключение. В мире Java это вызывает либо множество сбоев во время выполнения, либо заграждение оборонительной попытки.
Scala
xxxxxxxxxx
1
def methodWhichCanThrowException(): String = throw new RuntimeException
2
val aTry = Try(methodWhichCanThrowException())
3
// a try = "collection" with either a value if the code went well, or an exception if the code threw one
4
5
val anotherStringProcessing = aTry match {
6
case Success(validValue) => s"I have obtained a valid string: $validValue"
7
case Failure(ex) => s"I have obtained an exception: $ex"
8
}
Экземпляр Try может быть:
- Успех, содержащий реальную ценность, которую вы можете использовать
- или ошибка, содержащая исключение, которое вы можете затем обработать
Обратите внимание, что вместо сбоя приложения или создания исключения (что является побочным эффектом) мы вместо этого оборачиваем значение или исключение в небольшую структуру данных (мы описываем побочный эффект, если это произойдет).
Будущее
Фьючерсы были созданы для асинхронных вычислений, то есть вычислений, которые происходят в каком-то потоке, который вы не можете контролировать. Подобным образом мы можем думать о будущем как о «коллекции», содержащей элемент. На этот раз Future содержит элемент только тогда, когда вычисление значения заканчивается в потоке, который он выполняет.
Scala
xxxxxxxxxx
1
import scala.concurrent.ExecutionContext.Implicits.global
2
3
val aFuture = Future({
4
println("Loading...")
5
Thread.sleep(1000)
6
println("I have computed a value.")
7
67
8
})
Два примечания по приведенному выше коду:
- стиль кода: вы можете исключить скобки в конструкторе Future, когда блок кода является аргументом; скобки показаны для соответствия с основным синтаксисом, используемым до сих пор
- обратите внимание на импорт сверху; чтобы запустить Future, вам нужен ExecutionContext (= пул потоков), поэтому мы импортируем стандартный
В этом случае нет способа сопоставить образец с будущим, потому что вы не знаете, когда оно закончится. Вместо этого мы обычно сочетаем Futures с картой, flatMap и фильтром, так же, как мы делали со списками.
Мягкое вступление к последствиям
Это, вероятно, самая мощная функция компилятора Scala. Это настолько мощно, что часто опасно, поэтому грядущая Scala 3 сильно изменит язык в этой области. Это все еще будет поддерживаться, но не рекомендуется / будет удалено в какой-то момент (возможно, в версии 3.1 или более поздней).
Просто для проверки воды на последствиях, мы собираемся привести два примера использования для последствий.
Вариант использования 1: неявные аргументы. Метод, принимающий список неявных аргументов, будет нуждаться в том, чтобы компилятор автоматически извлекал значение для вставки этих аргументов. Пример:
Scala
xxxxxxxxxx
1
def aMethodWithImplicitArgs(implicit arg: Int) = arg + 1
2
3
implicit val myImplicitInt = 46
4
println(aMethodWithImplicitArgs) // aMethodWithImplicitArgs(myImplicitInt)
Обратите внимание, что метод принимает неявный Int в качестве аргумента. Учитывая, что я определил неявный Int перед вызовом метода, компилятор с удовольствием вставит это значение в вызов метода, поэтому мне не нужно предоставлять аргументы. Этот же механизм произошел с конструктором Future ранее (именно поэтому мы импортировали глобальное значение). В той же области должно быть только одно неявное значение данного типа. В противном случае компилятор будет жаловаться, потому что он не будет знать, какое значение вы хотели бы использовать.
Вариант использования 2: неявные преобразования. Это огромная сила расширения языка Scala, но она сопряжена с опасностями. Так что используйте эту функцию, как в следующем примере:
Scala
xxxxxxxxxx
1
implicit class MyRichInteger(n: Int) {
2
def isEven() = n % 2 == 0
3
}
4
println(23.isEven()) // new MyRichInteger(23).isEven()
6
// use this carefully
Неявный класс может принимать только один аргумент. Это по очень веской причине: после того, как вы сделаете неявный класс доступным (либо написав его, либо импортировав) в своей области видимости, компилятор сможет автоматически заключать Ints в экземпляры вашего неявного класса. В обычном коде вы пишете так, 23.isEven
как будто isEven был методом типа Int, но в действительности компилятор автоматически упаковывает число в неявный класс. Вы можете определить сколько угодно неявных классов, если у вас нет конфликтов методов.
Это приводит к тому, что система типов Scala выходит на совершенно новый уровень, поскольку позволяет расширять типы из библиотек, над которыми вы не имеете никакого контроля, или даже стандартных типов, таких как Int или String (как в приведенном выше примере). Неофициальный термин - «сводить» библиотеку, технический термин - «обогащение шрифтов» - используйте по своему усмотрению.
Это была Скала со скоростью света. Scala многое, намного больше, но я надеюсь, что эта серия статей была веселой и ценной!
- Даниэль для Rock the JVM