Страничка курса: https://maxcom.github.io/scala-course-2020/
Максим Валянский
окончил ВМиК МГУ в 2001 году;
занимаюсь разработкой ПО сколько себя помню;
работаю архитектором в компании
«Ростелеком-Солар».
Лекции
Семинары
Слайды, примеры программ и другие файлы доступны в репозитарии https://github.com/maxcom/scala-course-2020. Содержимое будет пополняться по ходу курса.
Для обсуждений и вопросов по курсу используем Telegram чат @scalacourse2020 (доступ по ссылке).
До Scala в продукте были:
Scheme (Lisp), OCaml,
Java, Python, C++ и др.
Хотелось:
Мы верим что функциональной программирование сделает наш софт качественнее, а разработку – более быстрой и предсказуемой.
Первый Scala компонент у нас появился осенью 2013 года в качестве эксперимента.
Это была распределенная система хранения бинарных данных - "файловое хранилище".
По результатам вывода в production эксперимент посчитали удачным.
Сейчас довольно большая часть продукта написана на Scala, и у нас много планов по развитию.
Scala — мультипарадигмальный язык программирования, спроектированный кратким и типобезопасным для простого и быстрого создания компонентного программного обеспечения, сочетающий возможности функционального и объектно-ориентированного программирования.
Работающий на JVM и хорошо интегрирующийся с существующим Java кодом
обо всём языке говорить долго
рассмотрим базовые возможности языка
и перейдем к практике
build.sbt -- настройки сборки
project -- еще настройки сборки
project/target -- кеш компилятора и вспомогательные файлы
src -- исходные файлы и ресурсы
src/main -- основной код
src/main/scala -- основной код на Scala
src/test -- исходные файлы и ресурсы тестов
src/test/scala -- код тестов на Scala
target -- результат компиляции
(live demo)
Синтаксис Scala: смесь Си + ML.
"Better Java", C, Python, ...
справочник по языку
или google + stackoverflow
object HelloWorld extends App {
println("Hello, world!")
}
(live demo)
// класс для JVM
// наследование от App
object HelloWorld extends App {
println("Hello, world!")
}
(live demo)
Не удается запустить в IDEA?
Отключите Gradle plugin
https://youtrack.jetbrains.com/issue/IDEA-228180
Блоки вместо выражений
object HelloWorld extends App {
println({
"Hello, world!"
})
}
(live demo)
object HelloWorld extends App {
println({
var str = "Hello, " // переменная
val add = "world!" // константа
str += add
str // "возвращается" последнее значение в блоке
})
}
(live demo)
Иммутательность "по-умолчанию":
объявляем функцию
object HelloWorld extends App {
println({
def square(x: Int) = x * x
square(10)
})
}
(live demo)
def square(x: Int) = x * x
функция - это значение:
val f: (Int => Int) = square
(её тип - Function1[Int, Int])
if тоже является выражением:
def abs(x: Int) = {
if (x >= 0) {
x
} else {
-x
} // и никакого "return"!
}
Типы выводятся в:
(кроме рекурсивных)
Рекомендуется указывать типы:
while
var a: Int = 0
while (a<10) {
a += 1
println(a)
} // бывает еще do { ... } while
(live demo)
for - не цикл, но "прикидывается" им:
for (i <- 0 to 10) {
println(i)
}
(live demo)
for (i <- List(1, 2, 3)) {
println(i)
}
(live demo)
for .. yield - "List comprehension" из Python
println(for (v <- List(1, 2, 3)) yield {
v * v
})
(live demo)
с условием
for {
v <- List(1, 2, 3, 4) if v%2 == 0
} yield {
v * v
}
(live demo)
писать на Scala - не сложно
Домашние задания:
независимые или
"большой" проект?
решим вместе на второй лекции
val pair: (Int, Int) = (1, 2)
// Tuple2[Int, Int]
деконструкция
val (first, second) = pair
бывают еще "тройки" и более - до 22
Seq[T] – общий тип для коллекций, имеющих определенный порядок (списки, массивы, вектора и т.п.)
ArrayBuffer[T] – аналог ArrayList из Java
Vector[T] – неизменяемый аналог ArrayList
val buffer = ArrayBuffer[Int](1, 2, 3)
buffer += 4 // добавление элемента
buffer(1) // получение элемента
// buffer еще и функция
val f: (Int ⇒ Int) = buffer
У стандартных коллекций много полезных функций
Справка на Vector: scaladoc.
val v = Vector(1, 2, 3, 4)
val (first, second) = v.splitAt(v.length / 2)
// first == Vector(1,2)
// second == Vector(3,4)
Механизм для создания собственных
типов для данных.
case class Address(`type`: String, value: String) {
def toStringAddress = s"${`type`}:$value"
}
Это не ООП! Данные не изменяемые, обычно не содержат бизнес-логики.
Собственный тип лучше, чем просто значения:
Address(`type`, value)
против пары
(String, String)
Собственный тип можно заводить и для простых значений, например:
case class UserId(uuid: UUID)
case class GroupId(uuid: UUID)
типы позволяют не путать значения между собой
Что есть в case class?
Можно разобрать класс обратно:
val address = Address("email", "abuse@sportloto.ru")
val Address(_, email) = address
object Address {
def apply(`type`: String, value: String) =
new Address(`type`, value)
def unapply(address: Address): Option[(String, String)] =
Some((address.`type`, address.value))
}
* объект-компаньон, заменяет static декларации Java
* для case классов apply/unapply создаются автоматически
Case классы можно объединить в иерархию
sealed trait Expr
case class Number(value: Int) extends Expr
case class Plus(lhs: Expr, rhs: Expr) extends Expr
case class Minus(lhs: Expr, rhs: Expr) extends Expr
* дальше будет более правильная версия этого примера
def value(expression: Expr): Int = expression match {
case Number(value) ⇒ value
case Plus(lhs, rhs) ⇒ value(lhs) + value(rhs)
case Minus(lhs, rhs) ⇒ value(lhs) - value(rhs)
}
val result = value(Plus(Number(2), Number(2)))
Pattern matching - альтернатива полиморфизму на методах:
trait Expr {
def eval: Int
}
case class Number(value: Int) extends Expr {
override val eval = value
}
case class Plus(lhs: Expr, rhs: Expr) extends Expr {
override def eval = lhs.eval + rhs.eval
}
case class Minus(lhs: Expr, rhs: Expr) extends Expr {
override def eval = lhs.eval - rhs.eval
}
Plus(Number(2), Number(2)).eval
Две модели:
Сделаем более правильное определение ADT
val number: Expr = Number(3)
val expr = Plus(Number(2), Number(2))
val buffer = ArrayBuffer(Number(1), expr)
// не компилируется
buffer += number
потому что тип buffer вот такой:
ArrayBuffer[Product with Serializable with Expr]
а изменяемые коллекции - инвариантные
запрещаем наследование - "final case class"
sealed trait Expr extends Product with Serializable
final case class Number(value: Int) extends Expr
final case class Plus(lhs: Expr, rhs: Expr) extends Expr
final case class Minus(lhs: Expr, rhs: Expr) extends Expr
более безопасная замена null
val v = Vector(1, 2, 3, 4, 5)
val r: Option[Int] = v.find(x => x > 2)
// r = Some(3)
плохой вариант работы с Option:
if (r.isDefined) {
println(r.get) // бросает исключение если значения нет
}
// 1
r match {
case Some(k) ⇒ println(k)
case None ⇒ println("None")
}
//
// 2
println(r.getOrElse("None"))
еще варианты рассмотрим на 3-м занятии
try {
1 / 0
} catch {
case ex: ArithmeticException ⇒
println(ex.getMessage)
1
}
try - тоже выражение, возвращает последнее значение
Тип Try
получим его так:
val result: Try[Int] = Try {
1 / 0
}
import scala.util.{Random, Try}
// fill - функция с двумя блоками параметров
// fill[A](n: Int)(elem: => A)
val vector = Vector.fill(10) {
Try {
1 / Random.nextInt(5)
}
}
vector.count(x => x.isSuccess)
Могут ли функции возвращать Try?
Да, но это "антипаттерн".
Описание алгоритма: на wikipedia
Неплохая визуализация
(надо выбрать "merge sort")
Ищем N самых меньших значений, не выполняя полной сортировки. Повторяющиеся значения не теряем.
Один проход по вектору, собираем N результатов по ходу движения.
Используем всё что найдем в стандартной библиотеке.
Решение должно давать те же результаты что и
topn(input: Vector[Int], n: Int) = input.sorted.take(n)
только без полной пересортировки
Модифицируем merge sort так, чтобы он выдавал только уникальные значения.
Дубли убираем в при merge.
Семинар 29-го января
Тема: разбор решений и проблем.
Unit-тесты и scalacheck.
Решение ждем в понедельник, в крайнем случае к семинару.
Простой вариант - m.valyanskiy@solarsecurity.ru.
Присылайте только исходники; код должен работать!
"Сложный" вариант - используем gitlab.com:
Напоминаю:
дополнительная часть, если успеем
“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live”
Всегда думайте о том, что
будет дальше с вашим кодом.
Две проблемы:
Большинство систем работают лучше всего, если они остаются простыми, а не усложняются.
Напоминаю: