scala для всех (РИФ 2015)

37
Scala для всех Арсений Жижелев, Мэйл.Ру Геймз

Upload: -

Post on 16-Apr-2017

318 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Scala для всех (РИФ 2015)

Scala для всех

Арсений Жижелев, Мэйл.Ру Геймз

Page 2: Scala для всех (РИФ 2015)

Предыстория

Page 3: Scala для всех (РИФ 2015)

Мартин Одерски (Martin Odersky, 1958) — немецкий учёный в области компьютерных наук и профессор EPFL в Швейцарии. Специализируется на статическом анализе кода и языках программирования. Разработал язык программирования Scala, поддержку Generics в Java и создал текущую версию javac, компилятора языка Java. В 2011 основал Typesafe, компанию, которая ставит своей целью поддержку и популяризацию языка Scala.

(Википедия)

Мартин Одерски

Мартин Одерски

Page 4: Scala для всех (РИФ 2015)

Scalable• лаконичный синтаксис упрощает написание интеграционных скриптов (в 2-4 раза короче Java) • практически все, что можно сделать в Java, можно сделать и в Scala• поддержка многопоточного программирования (immutable)• строгая типизация защищает от ошибок• развитые средства модульности и абстрагирования• основные конструкции языка используются и в маленьких программах, и в больших

Принципы Scala дизайна

"Глубокий", а не "широкий" язык• мало конструкций, но они очень мощные и выразительные.• "широта" охвата достигается за счёт библиотек.• Гибкость != простота

Page 5: Scala для всех (РИФ 2015)

A1: Scala = Java++

Java без ‘;’ вывод ‘;’ вывод типов case class’ы лаконичные объявления удобные коллекции

Java++ Полноценные generic’и с ко- и контра-

вариантностью cake pattern (альтернатива dependency

injection) вычисления на типах implicit’ы (type classes) HList‘ы метапрограммирование (макросы) …

Page 6: Scala для всех (РИФ 2015)

I. Scala: Примеры программ

Page 7: Scala для всех (РИФ 2015)

A0: Hello, World!

• Синглетоны• Тело объекта - конструктор• App – содержит main(args)• println – простая функция, объявлена в Predef

object Hello extends App { println("Hello, World!")}

Page 8: Scala для всех (РИФ 2015)

Scala:Простой калькулятор

Page 9: Scala для всех (РИФ 2015)

sealed abstract class Tree

case class BinOp(op: Op, l: Tree, r: Tree) extends Treecase class Var (n: String) extends Treecase class Const(v: Int) extends Tree

sealed abstract class Opcase object Add extends Opcase object Mul extends Op

Scala: expression tree (1)AST – абстрактное синтаксическое дерево

• Аргументы класса конструктор• case class – аргументы класса поля и свойства; equals и hashCode• case object – синглетон• sealed – все потомки объявлены в этом же файле• классы-однострочники!

Page 10: Scala для всех (РИФ 2015)

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• несколько списков аргументов

Page 11: Scala для всех (РИФ 2015)

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)

Page 12: Scala для всех (РИФ 2015)

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) }}

Page 13: Scala для всех (РИФ 2015)

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) }}

Page 14: Scala для всех (РИФ 2015)

Scala:Решето Эратосфена

Page 15: Scala для всех (РИФ 2015)

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• вывод типов

Page 16: Scala для всех (РИФ 2015)

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 – ленивые вычисления

Page 17: Scala для всех (РИФ 2015)

trait PrimeNumbersImplIsPrime extends PrimeNumbersDivisors { def isPrime(i:Int) = divisors(ensureRange(i)) == 1}

Scala: решето Эратосфена (3)

• наследование trait’а

Page 18: Scala для всех (РИФ 2015)

trait PrimeNumbersImplFactors extends PrimeNumbersDivisors { import scala.annotation.tailrec /** Разложение числа на множители. * @return список простых множителей, включая 1. */ def factors(i:Int) = { @tailrec def factors0(j:Int, result:List[Int]):List[Int] = if(divisors(j) == 1) 1 :: j :: result else factors0(j/divisors(j), divisors(j) :: result) factors0(ensureRange(i), List()) }}

Scala: решето Эратосфена (4)

• объявления – в любом месте, не засоряем область видимости, почти не нужны private

• оптимизация хвостовой рекурсии• tailrec – аннотация (компилятор

сообщит, если оптимизация не сработает)

• import в любом месте• правоассоциативные методы …:• конструктор списка :: = cons

Page 19: Scala для всех (РИФ 2015)

object PrimeNumbersAll extends PrimeNumbersDivisors with PrimeNumbersImplIsPrime with PrimeNumbersImplFactors { val MaxPrime = 1000}

Scala: решето Эратосфена (5)cake pattern

• собираем все объявления из trait’ов.

• даём значение абстрактному элементу

• линеаризация – решение diamond-проблемы

Page 20: Scala для всех (РИФ 2015)

Scala:Комплексные числа

Page 21: Scala для всех (РИФ 2015)

sealed trait Complex

case class CartesianComplex(x:Double, y:Double) extends Complexcase 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 – в другой

Page 22: Scala для всех (РИФ 2015)

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"

Page 23: Scala для всех (РИФ 2015)

Scala:Коллекции

Page 24: Scala для всех (РИФ 2015)

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 :: Nilassert(list == list2)

Page 25: Scala для всех (РИФ 2015)

val days2 = "Пн Вт Ср Чт Пт Сб Вс". split(" "). zipWithIndex. map{ case (day, index) => (index+1, Symbol(day)) }. toMapassert(days == days2)

val even = list.filter(_ % 2 == 0)

Scala: immutable коллекции (2)преобразования

• split – разбивает по regex'ам• zipWithIndex – к каждому

элементу приклеивает номер• map – применяет функцию к

каждому элементу• toMap – конвертирует коллекцию

Page 26: Scala для всех (РИФ 2015)

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

Page 27: Scala для всех (РИФ 2015)

Scala:Кортежи

Page 28: Scala для всех (РИФ 2015)

Scala: кортежи (1)деление с остатком

• (*,*) – • пара элементов, • тип пары элементов, • конструктор пары

элементов,• деконструктор пары

элементов• * -> * - конструктор пары

элементов

def divide(a:Int,b:Int):(Int, Int) = (a/b, a%b)

val (quotinent, residue) = divide(15,6)assert(residue == 3)

Page 29: Scala для всех (РИФ 2015)

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 1000val `[0,10,20..1000)` = 0 until 1000 by 10

Page 30: Scala для всех (РИФ 2015)

II. Scala: Разное

Page 31: Scala для всех (РИФ 2015)

Scala: persistent data structures

Page 32: Scala для всех (РИФ 2015)

Scala: ортогональный дизайн

Page 33: Scala для всех (РИФ 2015)

Scala: Кто использует?

http://typesafe.com/company/casestudies

Siemens Novell Atos Tumbler

Page 34: Scala для всех (РИФ 2015)

Scala: learning curve

“java без ;”

collections

Pattern matching

implicitsCovariant/contravariant

type algebra

время, лет

мас

терс

тво

Пример JavaLike

Page 35: Scala для всех (РИФ 2015)

Scala: производительность

Page 36: Scala для всех (РИФ 2015)

• 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)

Page 37: Scala для всех (РИФ 2015)

СПАСИБО ЗА ВНИМАНИЕ

[email protected]://github.com/Primetalk/SynapseGrid