Краткое введение в Scala для разработчиков на других языках. Рассмотрены несколько простых программ, написанных с использованием красивых возможностей Scala.
3. Мартин Одерски (Martin Odersky, 1958) —
немецкий учёный в области компьютерных наук
и профессор EPFL в Швейцарии.
Специализируется на статическом анализе
кода и языках программирования. Разработал
язык программирования Scala, поддержку
Generics в Java и создал текущую версию javac,
компилятора языка Java.
В 2011 основал Typesafe, компанию, которая
ставит своей целью поддержку и
популяризацию языка Scala.
(Википедия)
Мартин Одерски
Мартин Одерски
4. Scalable
• лаконичный синтаксис упрощает написание
интеграционных скриптов (в 2-4 раза короче
Java)
• практически все, что можно сделать в Java,
можно сделать и в Scala
• поддержка многопоточного программирования
(immutable)
• строгая типизация защищает от ошибок
• развитые средства модульности и
абстрагирования
• основные конструкции языка используются и в
маленьких программах, и в больших
Принципы Scala дизайна
"Глубокий", а не "широкий" язык
• мало конструкций, но они очень
мощные и выразительные.
• "широта" охвата достигается за счёт
библиотек.
• Гибкость != простота
5. A1: Scala = Java++
Java без ‘;’
вывод ‘;’
вывод типов
case class’ы
лаконичные объявления
удобные коллекции
Java++
Полноценные generic’и с ко- и контра-
вариантностью
cake pattern (альтернатива dependency
injection)
вычисления на типах
implicit’ы (type classes)
HList‘ы
метапрограммирование (макросы)
…
9. sealed abstract class Tree
case class BinOp(op: Op, l: Tree, r: Tree) extends Tree
case class Var (n: String) extends Tree
case class Const(v: Int) extends Tree
sealed abstract class Op
case object Add extends Op
case object Mul extends Op
Scala: expression tree (1)
AST – абстрактное синтаксическое дерево
• Аргументы класса конструктор
• case class – аргументы класса поля и
свойства; equals и hashCode
• case object – синглетон
• sealed – все потомки объявлены в этом же
файле
• классы-однострочники!
10. type Environment = String => Int
def eval(t: Tree, env: Environment): Int =
t match {
case BinOp(Add, l, r) =>
eval(l, env) + eval(r, env)
case BinOp(Mul, l, r) =>
eval(l, env) * eval(r, env)
case Var(n) => env(n)
case Const(v) => v
}
Scala: expression tree (2)
вычисление
• type alias (в т.ч. с аргументами)
• тип функции
• pattern matching
• несколько списков аргументов
11. val operations = Map[Op, (Int, Int) => Int](
Add -> ((x:Int, y:Int) => x+y),
Mul -> (_ * _)
)
def eval2(env: Environment)(t: Tree): Int =
t match {
case BinOp(op, l, r) =>
operations(op)(
eval2(env)(l), eval2(env)(r))
case Var(n) => env(n)
case Const(v) => v
}
Scala: expression tree (3)
операции как функции
// Div -> div(_,_)
// def div(x:Int, y:Int) = x/y
• Конструктор map’а
• функции-лямбды
• автоаргументы _
• несколько списков
аргументов – функцию
можно частично
применять (curry)
12. Scala: expression tree (4)
Разбор строки PEG
object SimpleExpressionsParser extends JavaTokenParsers {
def tree: Parser[Tree] = binOp | elem
def binOp: Parser[BinOp] = elem ~ op ~ elem ^^ {
case l ~ op ~ r => BinOp(op, l, r)
}
def elem = var1 | const
def op : Parser[Op] = '+' ^^ { case _ => Add } |
'*' ^^ { case _ => Mul }
def var1: Parser[Var] = ident ^^ Var
def const:Parser[Const] = wholeNumber ^^ {case n => Const(n.toInt)}
} def parse(str:String):Tree = parseAll(tree,str) match {
case Success(result) => result
case NoSuccess(msg,_) => throw new IllegalArgumentException(msg)
}
13. Scala: expression tree (5)
REPL – read eval print loop
object Calc extends App with CalcExpressions {
while(true){
val input = Console.readLine(">")
val tree = SimpleExpressionsParser.parse(input)
val result = eval(tree, empty)
println(result)
}
}
15. trait PrimeNumbers {
/** Параметр – максимальное рассматриваемое число.*/
val MaxPrime:Int
/** Проверка, i – простое? */
def isPrime(i:Int):Boolean
/** Разложение на множители.*/
def factors(i:Int):List[Int]
protected
def ensureRange(i:Int) = {
require(i<MaxPrime,
s"Argument i=$i is too big (>$MaxPrime).")
i
}
}
Scala: решето Эратосфена (1)
• trait – основа модульности +
dependency injection
• абстрактные объявления и
реализации
• require ~ assert
• string interpolation
• вывод типов
16. trait PrimeNumbersDivisors extends PrimeNumbers {
require(MaxPrime > 1, "Max prime should be > 1")
lazy val divisors = {
val arr = new Array[Int](MaxPrime)
arr(0) = 1
arr(1) = 1
for {i <- 2 to math.sqrt(MaxPrime).floor.toInt
if arr(i) == 0 // ==> i – простое
} {
arr(i) = 1 // 1 – признак простого числа
for {j <- i * i until MaxPrime by i
if arr(j) == 0} // j – нет делителей меньше
arr(j) = i // запомним первый делитель
}
arr.toSeq // превращаем в немодифицируемую коллекцию
}
}
Scala: решето Эратосфена (2)
• for – не цикл!
• for – что-то вроде объявления
множества: {x| x<10}
• метод to создаёт диапазон [2,
sqrt(MaxPrime)]
• метод by преобразует диапазон к
диапазону с шагом
• lazy val – ленивые вычисления
21. sealed trait Complex
case class CartesianComplex(x:Double, y:Double) extends Complex
case class PolarComplex(r:Double, phi:Double) extends Complex
def add(n1:CartesianComplex, n2:CartesianComplex) =
CartesianComplex(n1.x+n2.x, n1.y+n2.y)
def mul(n1:PolarComplex,n2:PolarComplex) =
PolarComplex(n1.r*n2.r, n1.phi+n2.phi)
Scala: комплексные числа (1)
• Чистые данные
• add удобнее делать в одной
форме
• mul – в другой
22. implicit def toPolar(c:Complex):PolarComplex = c match {
case CartesianComplex(x,y) =>
PolarComplex(math.sqrt(x*x+y*y), math.atan2(y,x))
case p:PolarComplex => p
}
implicit def toCartesian(c:Complex):CartesianComplex = ???
implicit class ComplexOps(c:Complex){
def +(other:Complex) =
add(c,other)
def *(other:Complex) =
mul(c,other)
}
Scala: комплексные числа (2)
implicit’ы
• implicit конвертация
• методы расширения
• операторы – тоже методы
• ??? – метод, генерирующий
исключение "not implemented"
24. Scala: immutable коллекции (1)
конструкторы
• конструкторы коллекций
соответствующих типов
• -> - создаёт пару
• 'str – создаёт Symbol("str")
• :: - правоассоциативный метод,
приклеивает новый элемент
слева от списка
• коллекции immutable (отдельно
есть комплект mutable-коллекций)
immutable – основа многопоточности
• val
• case class'ы
• immutable коллекции
val list = List(1,2,3,4,5,6)
val empty = Set()
val days = Map(1 -> 'Пн, 2 -> 'Вт,
3 -> 'Ср, 4 -> 'Чт,
5 -> 'Пт, 6 -> 'Сб,
7 -> 'Вс)
val list2 = 1 :: 2 :: 3 ::
4 :: 5 :: 6 :: Nil
assert(list == list2)
25. val days2 = "Пн Вт Ср Чт Пт Сб Вс".
split(" ").
zipWithIndex.
map{ case (day, index) =>
(index+1, Symbol(day)) }.
toMap
assert(days == days2)
val even = list.filter(_ % 2 == 0)
Scala: immutable коллекции (2)
преобразования
• split – разбивает по regex'ам
• zipWithIndex – к каждому
элементу приклеивает номер
• map – применяет функцию к
каждому элементу
• toMap – конвертирует коллекцию
26. Scala: immutable коллекции (3)
for
• divisors – ранее объявленная
коллекция (содержит
минимальный делитель индекса)
• (divisor, index) <-
извлекает из коллекции по-
элементно. Т.к. каждый элемент,
пара, то сразу разбивает пару на
две переменные – divisor, index
• yield включает значение в
результирующую коллекцию
• for … yield – определение
множества через свойства
элементов:
P = {index | index in N,
divisors(index)=1}
val primeNumbers =
for{ (divisor, index) <-
PrimeNumbersAll.divisors.toSeq.
zipWithIndex
if divisor == 1 &&
index>1 // исключаем 0 и 1
} yield index
28. Scala: кортежи (1)
деление с остатком
• (*,*) –
• пара элементов,
• тип пары элементов,
• конструктор пары
элементов,
• деконструктор пары
элементов
• * -> * - конструктор пары
элементов
def divide(a:Int,b:Int):(Int, Int) =
(a/b, a%b)
val (quotinent, residue) = divide(15,6)
assert(residue == 3)
29. Scala: кортежи (2)
• (*,*,*) – тройка
• каждый элемент имеет свой тип
• всего кортежи объявлены до 22.
• `4-ка` - объявление имени,
содержащего любые
спецсимволы. Удобно для
математических задач, например.
val triple = (1, "one", true)
assert(triple._3)
assert(triple._2.length == 3)
val `4-ка` = (1, true, List(3,4), 1->2)
assert(`4-ка`._3.head == 3)
val `[0..1000)` = 0 until 1000
val `[0,10,20..1000)` = 0 until 1000 by 10
34. Scala: learning curve
“java без ;”
collections
Pattern matching
implicits
Covariant/contravariant
type algebra
время, лет
мастерство
…
Пример JavaLike
36. • Slick 3.0 – типизированный доступ к базам данных
• Akka – параллельные и распределённые системы
• SynapseGrid – фреймворк для описания параллельных систем реального времени (FRP)
• Play – веб-фреймворк (aka «Ruby on Rails на стероидах») (есть ещё Spray, Lift, Scalatra и др.)
• Apache Spark – Big Data фреймворк
• Shapeless, Scalaz, Monocle – библиотеки для работы с типами высших порядков
• ScalaFX – библиотека для создания GUI на основе JavaFX (есть ещё Scala-swing)
• Parboiled2 – создание parser-комбинаторов
• ScalaTest, Specs2 – библиотеки для создания тестов
• Scala Async – библиотека для написания линейного кода с блокировками, который будет
преобразован в код без блокировок
• Scala.js – кросс-компилятор Scala в JavaScript
Scala: библиотеки
(см. github/scala-awesome)
РИФ
Created Tuesday 08 September 2015
http://www.scala-lang.org/old/node/104 - список тем
Доклад "Магия Scala"/Scala Awesome
Орг.вопросы
Изменить название доклада
Темы, которые хотелось бы раскрыть:
1. Небольшое введение
предыстория языка, профессор EPFL Мартин Одерский, фотография (http://lampwww.epfl.ch/~odersky/). Scalable. http://www.slideshare.net/Odersky/scala-the-simple-parts
+ Hello World
+trait'ы
всё есть expression. Обеспечивается произвольная композиция элементов
+scope'ы вместо private. Можно объявлять имена на любом уровне и они не будут засорять namespace.
+минималистический унифицированный синтаксис
+вывод типов
кортежи
+case class'ы
унифицированные коллекции,
+for-comprehension.
+object'ы
FP
+immutable
+concise
+функции первоклассные
REPL
+паттерн-матчинг! Декомпозиция данных, включая сложноструктурированные.
+символы/операторы - методы (пример - комплексные числа, case class, операторы)
+extension-методы
Зрелость языка: Язык работает в промышленном программировании и чувствует себя прекрасно.
Жемчужины: http://stackoverflow.com/questions/1025181/hidden-features-of-scala -
regex'ы в паттерн матчинге
типы высших порядков
структурные типы
коллекции после преобразования автоматически правильного типа.
программирование на типах
+оптимизация хвостовой рекурсии
async-await. И что интересно - Библиотека! (reset/shift)
2. Web
Play
Hello World
Stateless, асинхронность,
Lift, Scalatra,
3. Библиотеки
-Parboiled2!
Slick?
Akka
Synapse Grid
ещё какие-то из списка Scala Awesome
ScalaTest, Specs2
- язык, разработанный профессором, вызывает доверие
https://www.artima.com/scalazine/articles/origins_of_scala.html
https://www.artima.com/scalazine/articles/goals_of_scala.html
Scala is flexible. Flexibility comes at the price of simplicity (https://medium.com/@kvnwbbr/transitioning-to-scala-d1818f25b2b7)
Scripting
Scala is great for scripting because its type inference and functional style make it both concise and great for slicing and dicing collections etc.
Applications
Scala is great for writing applications because it is a statically type checked language with some great features for working in both functional and imperative styles. Scala is compatible with Java and hence you can leverage an enormous ecosystem of libraries.
Enterprise Deployment
Scala compiles to bytecode and runs on the Java Virtual Machine, a stable system with great performance, when compared to (so-called) dynamically typed (i.e. untyped) languages. Again, the fact that it is statically typed means that refactoring is made much easier on large codebases.
(http://programmers.stackexchange.com/questions/130335/why-is-scala-more-scalable-than-other-languages)
сокращение пространства состояний
Zero debug, т.к. функция не зависит от неизвестного состояния