Scala для всех (РИФ 2015)
Transcript of Scala для всех (РИФ 2015)
Scala для всех
Арсений Жижелев, Мэйл.Ру Геймз
Предыстория
Мартин Одерски (Martin Odersky, 1958) — немецкий учёный в области компьютерных наук и профессор EPFL в Швейцарии. Специализируется на статическом анализе кода и языках программирования. Разработал язык программирования Scala, поддержку Generics в Java и создал текущую версию javac, компилятора языка Java. В 2011 основал Typesafe, компанию, которая ставит своей целью поддержку и популяризацию языка Scala.
(Википедия)
Мартин Одерски
Мартин Одерски
Scalable• лаконичный синтаксис упрощает написание интеграционных скриптов (в 2-4 раза короче Java) • практически все, что можно сделать в Java, можно сделать и в Scala• поддержка многопоточного программирования (immutable)• строгая типизация защищает от ошибок• развитые средства модульности и абстрагирования• основные конструкции языка используются и в маленьких программах, и в больших
Принципы Scala дизайна
"Глубокий", а не "широкий" язык• мало конструкций, но они очень мощные и выразительные.• "широта" охвата достигается за счёт библиотек.• Гибкость != простота
A1: Scala = Java++
Java без ‘;’ вывод ‘;’ вывод типов case class’ы лаконичные объявления удобные коллекции
Java++ Полноценные generic’и с ко- и контра-
вариантностью cake pattern (альтернатива dependency
injection) вычисления на типах implicit’ы (type classes) HList‘ы метапрограммирование (макросы) …
I. Scala: Примеры программ
A0: Hello, World!
• Синглетоны• Тело объекта - конструктор• App – содержит main(args)• println – простая функция, объявлена в Predef
object Hello extends App { println("Hello, World!")}
Scala:Простой калькулятор
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 – все потомки объявлены в этом же файле• классы-однострочники!
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• несколько списков аргументов
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)
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) }}
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) }}
Scala:Решето Эратосфена
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• вывод типов
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 – ленивые вычисления
trait PrimeNumbersImplIsPrime extends PrimeNumbersDivisors { def isPrime(i:Int) = divisors(ensureRange(i)) == 1}
Scala: решето Эратосфена (3)
• наследование trait’а
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
object PrimeNumbersAll extends PrimeNumbersDivisors with PrimeNumbersImplIsPrime with PrimeNumbersImplFactors { val MaxPrime = 1000}
Scala: решето Эратосфена (5)cake pattern
• собираем все объявления из trait’ов.
• даём значение абстрактному элементу
• линеаризация – решение diamond-проблемы
Scala:Комплексные числа
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 – в другой
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"
Scala:Коллекции
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)
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 – конвертирует коллекцию
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
Scala:Кортежи
Scala: кортежи (1)деление с остатком
• (*,*) – • пара элементов, • тип пары элементов, • конструктор пары
элементов,• деконструктор пары
элементов• * -> * - конструктор пары
элементов
def divide(a:Int,b:Int):(Int, Int) = (a/b, a%b)
val (quotinent, residue) = divide(15,6)assert(residue == 3)
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
II. Scala: Разное
Scala: persistent data structures
Scala: ортогональный дизайн
Scala: Кто использует?
http://typesafe.com/company/casestudies
Siemens Novell Atos Tumbler
Scala: learning curve
“java без ;”
collections
Pattern matching
implicitsCovariant/contravariant
type algebra
время, лет
мас
терс
тво
…
Пример JavaLike
Scala: производительность
• 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)
СПАСИБО ЗА ВНИМАНИЕ
[email protected]://github.com/Primetalk/SynapseGrid