2. ОБО МНЕ
• Scala с 2011
• Один из организаторов встреч Scala Moscow
User Group
• Работаю в Qubell
3. О ВАС
• Кто из вас
• пишет на Java?
• слышал о Scala?
• пишет на Scala?
• слышал что такое монада?
• понимает, что монада это моноид в категории эндофункторов?
4. ЧТО ТАКОЕ SCALA
• Язык существует с 2003г.
• Scalable language
• Объединяет в себе OO и ФП-концепции
• Статически типизирован
• Компилируется в java-байткод
• Open source
5. ПЕРЕМЕННЫЕ И ЗНАЧЕНИЯ
var a: String = "foo"!
a = "bar" // разрешено!
!
val b: String = "foo"!
b = "bar" // запрещено
6. ФУНКЦИИ
def sum1(a: Int, b: Int): Int =!
a + b!
!
val sum2 =!
(a: Int, b: Int) => a + b!
!
val sum3: (Int, Int) => Int =!
(a, b) => a + b!
!
val sum4: (Int, Int) => Int =!
_ + _
7. КЛАССЫ, ОБЪЕКТЫ И
ТРЕЙТЫ
trait Dumpable {!
def dump: String!
def dump(out: PrintStream) { out.print(dump) }!
}!
!
class Foo extends Dumpable {!
override def dump = "Foo"!
}!
!
val foo = new Foo!
foo.dump(Console.out)!
!
object Bar extends Dumpable {!
override def dump = "Bar"!
}!
!
Bar.dump(Console.err)
8. СИСТЕМА ТИПОВ
• Всё есть объект:Any,AnyVal,AnyRef
• Кортежи:
scala> val tuple = (1, "a", true)!
tuple: (Int, String, Boolean) = (1,a,true)!
!
scala> tuple._2!
res0: String = a!
!
scala> val (x, y, _) = tuple!
x: Int = 1!
y: String = a
12. СОВМЕСТИМОСТЬ
• Работает на платформе JVM
• Windows, Mac, Linux,Android, кофеварки
• Отличный JIT-компилятор
• Отличный GC
• Прозрачно интегрируется с Java-кодом
• Смешанная компиляция
• Можно использовать все доступные java-библиотеки
• Старый код можно тоже не переписывать
13. ПРОИЗВОДИТЕЛЬНОСТЬ
• Какой код напишете, так и будет
• Производительность сравнима с Java
• Обычно потребляет немного больше памяти,
чем Java
16. РАЗМЕР КОДА JAVA
public class Person {!
!
private final String name;!
private final int age;!
!
public Person(String name, int age) {!
this.name = name;!
this.age = age;!
}!
!
public String getName() {!
return name;!
}!
!
public int getAge() {!
return age;!
}!
!
@Override!
public String toString() {!
return "Person(" + name + ", " + age + ")";!
}!
!
@Override!
public boolean equals(Object o) {!
if (this == o) return true;!
if (o == null || getClass() != o.getClass()) return false;!
Person person = (Person) o;!
if (age != person.age) return false;!
if (name != null ? !name.equals(person.name) : person.name != null) return false;!
return true;!
}!
!
@Override!
public int hashCode() {!
int result = name != null ? name.hashCode() : 0;!
result = 31 * result + age;!
return result;!
}!
!
}
17. РАЗМЕР КОДА SCALA
+ Метод-фабрика
+ Метод copy
+ Деконструктор для pattern matching
case class Person(name: String, age: Int)
18. РАЗМЕР ИМЕЕТ ЗНАЧЕНИЕ
• Число ошибок на строку кода — константа
слабо зависящая от языка программирования
• В поле зрения человека попадает
ограниченный объём кода
• Человек может сфокусировать внимание на
определённом объёме информации
19. ВЫВОД ТИПОВ
• Scala
! ! val list = new ListBuffer[String]()!
• Java
! ! ArrayList<String> list = new ArrayList<>();!
• Scala
! ! val set = new HashMap[String, Int]().keySet!
• Java
! ! Set<String> set = new HashMap<String, String>().keySet();!
20. OPTION
• Опциональное значение: None или Some(value)
• Многие операции можно делать не извлекая значение
• Извлечь тоже можно, но в отличие от null это
делается явно
• Все библиотеки используют этот тип
• В Java 8 тоже есть Optional
21. OPTION
val capitals = Map("Russia" -> "Moscow",!
"France" -> "Paris")!
!
val a = capitals.get("Russia")!
// Some("Moscow")!
val b = capitals.get("Italy") // None!
!
a.map(_.toUpperCase) // Some("MOSCOW")!
b.map(_.toUpperCase) // None!
!
a.getOrElse("Unknown") // "Moscow"!
b.getOrElse("Unknown") // "Unknown"!
!
for (x <- a; y <- b) yield s"$x, $y" // None
22. PATTERN MATCHING
sealed trait Shape!
case class Circle(radius: Double) extends Shape!
case class Rectangle(width: Double,!
height: Double) extends Shape!
!
val description = shape match {!
case Rectangle(w, h) if w == h =>!
s"square, side $w"!
case Rectangle(w, h) =>!
s"rectangle, $w x $h"!
case Circle(r) =>!
s"circle, radius $r"!
}
23. ПРОВЕРКА ПОЛНОТЫ
sealed trait Shape!
case class Circle(radius: Double) extends Shape!
case class Rectangle(width: Double,!
height: Double) extends Shape!
!
val description = shape match {!
case Circle(r) =>!
s"circle, radius $r"!
}!
!
pm.scala:16: match may not be exhaustive.!
It would fail on the following input: Rectangle(_, _)!
val description = shape match {!
^
24. FOR-EXPRESSIONS
case class City(name: String,!
streets: List[Street],!
population: Int)!
!
case class Street(name: String,!
sights: List[String])!
!
val cities: List[City]!
!
val plan = cities!
.filter(_.population > 1000)!
.flatMap(_.streets)!
.flatMap(s => s.sights.map(s -> _))
25. FOR-EXPRESSIONS
val plan = cities!
.filter(_.population > 1000)!
.flatMap(_.streets)!
.flatMap(s => s.sights.map(s -> _))!
!
val plan = for {!
city <- cities if city.population > 1000!
street <- city.streets!
poi <- street.sights!
} yield (street.name, poi)
26. НЕ ТОЛЬКО КОЛЛЕКЦИИ
def httpGet(url: String): Future[String] = ???!
!
def getPost(idx: Int): Future[String] =!
for {!
posts <- httpGet("http://localhost/posts")!
slug = posts.split("n")(idx)!
post <- httpGet(s"http://localhost/$slug")!
} yield post
27. AD-HOC POLYMORPHISM
trait Pretty {!
def toPrettyString: String!
}
Привет, Мартин. Ты не мог бы
отнаследовать классы стандартной
библиотеки от Pretty? Это очень
важно для нас…
33. БОРЬБА СО СЛОЖНОСТЬЮ
• Обучение → делает сложное простым
• Административные меры → запрет писать
сложный код
• Code review → понятен ли ваш код другим
• Поддержка компилятора → явное подключение
некоторых языковых конструкций
34. ENABLING LANGUAGE
FEATURES
implicit def intToString(x: Int): String = x.toString!
!
val x: String = 1
features.scala:7: implicit conversion method
intToString should be enabled by making the implicit
value scala.language.implicitConversions visible.
import scala.language.implicitConversions
35. КАДРОВЫЙ ВОПРОС
• Перейти с Java на Scala для большинства не
составляет проблемы
• Готовых Scala-разработчиков мало, но их число
растёт
• Конкуренция на рынке вакансий ниже
• Средний уровень Scala-разработчиков выше
36. КАДРОВЫЙ ВОПРОС
• Нужна небольшая талантливая команда —
Scala может оказаться предпочтительнее
• Большая команда для несложных задач —
Java будет лучшим выбором
37. УГОВОРИЛ, КАК НАЧАТЬ
• Пишите на Scala так же как на Java
• Начните с тестов
• Поощряйте «хороший» скала-код: Option вместо null,
иммутабельность
• Желательно наличие эксперта в команде
• Некоторые IDE умеют конвертировать Java код в Scala
автоматически
38. ПРОБЛЕМЫ
• Более сложные концепции
• Медленная компиляция
• Бинарная совместимость
• Недостаточная поддержка в IDE
39. ПОЛЕЗНЫЕ БИБЛИОТЕКИ
• Akka — многопоточные распределённые приложения
• Slick — работа с базами данных
• Shapeless, scalaz — выразительный и обобщённый код
• Scalatest — удобное тестирование
• SBT — система сборки
40. AKKA
• Акторы — легковесные сущности, асинхронно
обменивающиеся сообщениями
• О потоках и синхронизации заботится Akka
• Простой переход к распределённому
приложению
• Устойчивость к ошибкам
41. ПРИМЕР AKKA
class Worker extends Actor {!
!
val db = DB.connect()!
!
override def postStop() {!
db.close()!
}!
!
def receive = {!
case Query(sql) => sender() ! db.query(sql)!
}!
!
}
42. СУПЕРВИЗОР
class Supervisor extends Actor {!
!
override val supervisorStrategy =!
OneForOneStrategy(maxNrOfRetries = 10,!
withinTimeRange = 1 minute) {!
case _: IOException => Restart!
case _: Exception => Escalate!
}!
!
val worker = context.actorOf(Props[Worker])!
!
def receive = {!
case q: Query => worker forward q!
}!
!
}
45. SLICK
• Схема и запросы — обычный Scala-код
• Работа с базой данных так же проста, как с
обычными коллекциями
• Запросы типобезопасны
46. ПРИМЕР SLICK
// select NAME from COFFEES !
// where PRICE < 10.0!
// order by NAME!
coffees!
.filter(_.price < 10.0)!
.sortBy(_.name)!
.map(_.name)!
.list!
!
// Seq[String]("Cappuccino", "Espresso")
47. БИБЛИОТЕКИTYPELEVEL
• Scalaz — упрощает программирование в
функциональном стиле (монады, функторы,
линзы и другие классы типов)
• Shapeless — ещё более строгая типизация
48. SCALAZ: MONOIDS
val x = Map("a" -> List(1))!
!
val y = Map("a" -> List(2),!
"b" -> List(3))!
!
// or println(x mappend y)!
println(x |+| y)!
!
"Map(a -> List(1, 2), b -> List(3))"
50. SCALATEST
class ExampleSpec extends FreeSpec with Matchers {!
"A Stack" should "pop values in last-in-first-out order" in {!
val stack = new Stack[Int]!
stack.push(1)!
stack.push(2)!
stack.pop() should be (2)!
stack.pop() should be (1)!
}!
it should "throw NoSuchElementException if an empty stack is popped" in {!
val emptyStack = new Stack[Int]!
a [NoSuchElementException] should be thrownBy {!
emptyStack.pop()!
} !
}!
}
51. SCALATEST
$ scala -cp scalatest_2.11-2.2.0.jar org.scalatest.run ExampleSpec
Discovery starting.
Discovery completed in 21 milliseconds.
Run starting. Expected test count is: 2
ExampleSpec:
A Stack
- should pop values in last-in-first-out order
- should throw NoSuchElementException if an empty stack is popped
Run completed in 76 milliseconds.
Total number of tests run: 2
Suites: completed 1, aborted 0
Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
All tests passed.
52. ASSERTIONS
scala> assert(a == b || c >= d)
org.scalatest.exceptions.TestFailedException:
1 did not equal 2, and 3 was not greater than or equal to 4
at ...
!
scala> assert(Some(2).isEmpty)
org.scalatest.exceptions.TestFailedException:
Some(2) was not empty
at ...
53. DIAGRAMMED ASSERTIONS
scala> assert(a == b || c >= d)
org.scalatest.exceptions.TestFailedException:
!
assert(a == b || c >= d)
| | | | | | |
1 | 2 | 3 | 4
| | false
| false
false
!
at ...