Язык программирования Scalа
для создания успешных Интернет-
проектов
Успенский
Владимир
О нашем проекте
Старт — сентябрь 2011
Запуск — май 2012
Итого 9 месяцев
▶ Сразу лучший Интернет-банк в России
▶ Хорошие отзывы клиентов
▶ Бешенное развитие в течение года:
функционал, безопасность, интерфейс
и это не конец...
Общие слова о Scala
Один из альтернативных языков на JVM,
привносит функциональный подход
ООП и ФП дополняют друг друга
ООП чище чем в Java:
все значения — объекты,
все операции — вызов метода
Функциональный != Процедурный
Book
Итак, мы начинаем проект...
BookStore
sell
Author
Money accept
Придумаем модель предметной области:
Просто, правда?
has
Количество кода, обозримость
case class Money(amount: Long, currency: Currency)
case class Author(name: String)
case class Book(name: String, author: Author)
trait BookStore {
def buy(bookName: String, money: Money): Book
}
Чтобы иметь интерфейсы под рукой,
можно объявить всё в одном файле:
Всё ещё просто!
Количество кода, обозримость
WAT?
Вывод типов
Map<String, List<String>> booksAuthors =
new HashMap<String, List<String>>();
List<String> authors = new ArrayList(1);
authors.add("Herman Melville");
booksAuthors.put("Moby-Dick", authors);
for(Map.Entry<String, List<String>> bookAuthors :
booksAuthors.entrySet()) {
String book = bookAuthors.getKey();
String authors = bookAuthors.getValue();
...
}
Вывод типов
val booksAuthors = Map("Moby-Dick" ->
List("Herman Melville"))
for((book, authors) <- booksAuthors) {
...
}
Ничего лишнего!
Коллекции
val somethingToRead =
books.filter(isPhysics)
.groupBy(_.author).map({
case (author, books) =>
books.sortBy(rating(author)).head
}).headOption
Console.print(somethingToRead.firstPage)
Стандартные методы на все случаи жизни:
Коллекции
val somethingToRead =
books.filter(isPhysics)
.groupBy(_.author).map({
case (author, books) =>
books.sortBy(rating(author)).head
}).headOption
Console.print(somethingToRead.firstPage)
Стандартные методы на все случаи жизни:
Коллекции
val somethingToRead =
books.filter(isPhysics)
.groupBy(_.author).map({
case (author, books) =>
books.sortBy(rating(author)).head
}).headOption
Console.print(somethingToRead.map(_.firstPage)
.getOrElse("Nothing is there!"))
Стандартные методы на все случаи жизни:
JSON
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
val json = parse("""{ "name": "joe",
"address": {
"street": "Boulevard",
"city": "Helsinki"
}
}""")
assert (json.extract[Person] ==
Person(joe, Address("Buolevard", "Helsinki"))
(Scala 2.10, Json4s)
XML
val xml = <root><tag attr="value">text</tag></root>
val xml = XML.loadString("""<root>
<tag attr="value">text</tag>
</root>""")
XPath
assert ((xml  "tag").text == "text")
assert ((xml  "tag"  "@attr").text == "value")
Pimp my library!
import ru.tcs.db.extensions._
val userId = resultSet.getId[User]("user_id")
// userId: Id[User]
val balance = resultSet.getMoney("balance")
// balance: MoneyAmount
Берём обычный java.sql.ResultSet
Retroactive extension для любого типа!
Абстракция
▶ Способность находить и описывать общее, чтобы
- экономить время, переиспользуя, или при правках
- и недопускать разного поведения и ошибок
▶ Обычно применяются: наследование,
параметризация, параметризация типа (generics)
▶ Замыкания и функции над функциями
позволяют параметризовать код другим кодом,
давая доступ к новому механизму абстракции
Функции высшего порядка
// где-то в определениии Iterable[T]...
def filter(predicate: T => Boolean):Iterable[T]
def map[A](transform: T => A): Iterable[A]
Операции с коллекциями так удобно использовать,
как раз потому, что стандартные функции можно
параметризировать своим поведеднием
Примеси/Mixins
class Animal
trait Philosophical {
def philosophize() {
Console.println(
"It ain't easy being " + toString)
}
}
class Frog extends Animal with Philosophical {
override def toString = "green"
}
new Frog().philosophize()
// It ain't easy being green
Помогают выносить
общий функцинал
без недостатков
множественного
наследования
Higher Kinds
trait Factory[T] {
def create(): T
}
Параметр типа первого порядка:
trait Functor[H[_]] {
def map[A,B](fn: A => B)(fa: H[A]): H[B]
}
Параметр типа высшего порядка:
Корректность и тотальность
Всё никогда не бывает хорошо
"A good programmer is someone who looks both ways
before crossing a one-way street." ~ Doug Linder
Тотальность — свойство программы, быть
определённой для всех входных параметров
Scala использует типизацию (type-safety) для проверки
корректности при компилляции, где это возможно
Маленький мотивирующий пример
Book book = bookShelf.get("Moby-Dick");
Reader reader = readersQueue.peek();
if(reader.favorite.equals(book.author)) {
reader.read(book);
}
Маленький мотивирующий пример
Book book = bookShelf.get("Moby-Dick");
Reader reader = readersQueue.peek();
if(reader.favorite.equals(book.author)) {
reader.read(book);
}
Значения может просто не быть!
Если значение необязательно,
лучше предусмотреть это в модели типов
Маленький мотивирующий пример
val book: Option[Book] = bookShelf.get("Moby-Dick")
val reader: Option[Reader] = readersQueue.peek
if(reader.favorite.equals(book.author)) {
reader.read(book)
}
Теперь программа просто не собралась
Разработчик узнал об ошибках сразу,
до передачи в тестирование или переноса в бой
Управление непредвиденным
При выполнении вычислений возможно:
Возможностью таких исходов можно управлять
с помощью типа возвращаемого значения.
Такие значения можно создавать и связывать.
Тип вместе с операциями называется Монадой.
▶ ничего не получить, — Option
▶ получить исключение, — Try
▶ занимать время, — Future
▶ работать с диском — IO
etc.
Более строгие ограничения на типы
▶ Параметр типа обязательно должен быть
Всегда List[T], но не List
▶ Higher Kinds
Не надо переходить к Object
▶ Вариантность
Не обязательно переходить к List[_]
Вариантность
case class Invariant[T]()
case class Covariant[+T]()
case class Contravariant[-T]()
val in: Invariant[Object] = Invariant[String]
val in: Invariant[String] = Invariant[Object]
val co: Covariant[Object] = Covariant[String]
val co: Covariant[String] = Covariant[Object]
val contra: Contravariant[Object] = Contravariant[String]
val contra: Contravariant[String] = Contravariant[Object]
Scala позволяет указать вариантность для параметров типа:
Immutability
▶ Возможность изменения выбирается явно:
▶ Коллекции по умолчанию immutable
val immutable = ...
var mutable = ...
List, Set, Map,
▶ Immutability — невозможность изменить объект
или ссылку после создания.
есть также mutable версии
Достоинства Immutability
▶ Меньше ошибок из-за переиспользования
переменных для разных целей
▶ Операции проще и однороднее:
нет особенностей, связанных с изменением
▶ Просто работать в многопоточной среде,
нет расходов на синхронизацию,
возникающих из-за concurrent mutable state
Адаптация к изменениям
Когда, проект запущен
Клиенты начали пользоваться и писать отзывы
Бизнес хочет зарабатывать на продукте
А несколько раз в год, запускаются крупные проекты
Вам нужно быть очень гибкими!
Адаптация к изменениям
Если приходится что-то серьёзно менять,
когда меньше кода, лучше видны последствия,
а, благодаря типам, изменения ещё и проверяются
компиллятором — меньше вероятность сломать.
Выше уровень абстракциии и однородный код —
нет необходимости влезать в низкоуровневые детали
реализации, можно охватить больше случаев
меньшими усилиями.
Группировка запросов и
параллельная обработка
Задача:
«Сделать возможность параллельной
обработки запросов, когда все запросы
обработаны, нужно собрать результаты»
Группировка запросов и
параллельная обработка
ThreadPool?
CyclicBarrier?
CountDownLatch?
Задача:
«Сделать возможность параллельной
обработки запросов, когда все запросы
обработаны, нужно собрать результаты»
Похоже,
это сложная
задача!
Группировка запросов и
параллельная обработка
val responses =
requests.par.map(processRequest).seq
Задача:
«Сделать возможность параллельной
обработки запросов, когда все запросы
обработаны, нужно собрать результаты»
Нужно собирать информацию
со множества бекендов
Продукты
CRM
Операционные
данные
Предложения
Нужно собирать информацию
со множества бекендов
Продукты
CRM
Операционные
данные
Предложения
Очень долго!
Нужно собирать информацию
со множества бекендов
Продукты
CRM
Операционные
данные
Предложения
Отлично!
Нужно собирать информацию
со множества бекендов
Продукты
CRM
Операционные
данные
Предложения
Отлично!
ThreadPool?
CyclicBarrier?
CountDownLatch?
Похоже это
сложная задача!
Мне нужно
несколько дней!
Нужно собирать информацию
со множества бекендов
Распараллелить долгие вызовы для одного,
не самого простого, случая удалось за 30 минут
val (operations, offers) = Await.result(
future {
operationsRepo.readOperations(userId)
} zip future {
offersRepo.readOffers(userId)
}, 60 seconds)
Расширение команды
Новые сотрудники втягиваются за пару недель:
- всё не так сильно отличается,
- меньше кода и неявных контрактов
почти за два года команда
люди менялись в процессе
выросла почти с нуля,
Недостатки
▶ Есть неочевидные местаx
Надо читать документацию и писать тесты
▶ Длинные цепочки вызовов тяжело отлаживать,
что заставляет выносить переменные
и разделять код на небольшие методы
Недостатки
▶ Есть неочевидные местаx
Надо читать документацию и писать тесты
▶ Длинные цепочки вызовов тяжело отлаживать,
что заставляет выносить переменные
и разделять код на небольшие методы
Что?
тесты и небольшие
методы? да ладно!
Недостатки
▶ Есть неочевидные местаx
Надо читать документацию и писать тесты
▶ Длинные цепочки вызовов тяжело отлаживать,
что заставляет выносить переменные
и разделять код на небольшие методы
▶ Ограниченная поддержка генерации кода в IDE,
впрочем генерировать код не очень актуально
▶ На рынке не так много специалистов по Scala,
приходится искать заинтересованных ребят,
которые хотят делать что-то новое
▶ Та же платформа
▶ Те же среды разработки
▶ Та же релизная политика
▶ Доступен код на Java, все библиотеки JVM,
старое доброе ООП и императивность
мало того, с этого стоит начать
Перейти просто
администраторам всё знакомо
не нужны новые лицензии
тестировщики и администраторы в покое
если что, всегда есть к чему вернуться,
Итог
▶ Проще код
▶ Меньше ошибок
▶ Меньше времени на типовые вещи
▶ Простая работа с потоками
▶ Просто поддерживать рост
▶ Легко попробовать и перейти
Итог
Scala — отличный инструмент
для успешного проекта!
scala-lang.org/downloads
scala> questions
e-mail: v.uspenskiy@tcsbank.ru
skype: tcs_uspenskiy

Язык программирования Scala / Владимир Успенский (TCS Bank)

  • 1.
    Язык программирования Scalа длясоздания успешных Интернет- проектов Успенский Владимир
  • 2.
    О нашем проекте Старт— сентябрь 2011 Запуск — май 2012 Итого 9 месяцев ▶ Сразу лучший Интернет-банк в России ▶ Хорошие отзывы клиентов ▶ Бешенное развитие в течение года: функционал, безопасность, интерфейс и это не конец...
  • 5.
    Общие слова оScala Один из альтернативных языков на JVM, привносит функциональный подход ООП и ФП дополняют друг друга ООП чище чем в Java: все значения — объекты, все операции — вызов метода Функциональный != Процедурный
  • 6.
    Book Итак, мы начинаемпроект... BookStore sell Author Money accept Придумаем модель предметной области: Просто, правда? has
  • 7.
    Количество кода, обозримость caseclass Money(amount: Long, currency: Currency) case class Author(name: String) case class Book(name: String, author: Author) trait BookStore { def buy(bookName: String, money: Money): Book } Чтобы иметь интерфейсы под рукой, можно объявить всё в одном файле: Всё ещё просто!
  • 8.
  • 9.
    Вывод типов Map<String, List<String>>booksAuthors = new HashMap<String, List<String>>(); List<String> authors = new ArrayList(1); authors.add("Herman Melville"); booksAuthors.put("Moby-Dick", authors); for(Map.Entry<String, List<String>> bookAuthors : booksAuthors.entrySet()) { String book = bookAuthors.getKey(); String authors = bookAuthors.getValue(); ... }
  • 10.
    Вывод типов val booksAuthors= Map("Moby-Dick" -> List("Herman Melville")) for((book, authors) <- booksAuthors) { ... } Ничего лишнего!
  • 11.
    Коллекции val somethingToRead = books.filter(isPhysics) .groupBy(_.author).map({ case(author, books) => books.sortBy(rating(author)).head }).headOption Console.print(somethingToRead.firstPage) Стандартные методы на все случаи жизни:
  • 12.
    Коллекции val somethingToRead = books.filter(isPhysics) .groupBy(_.author).map({ case(author, books) => books.sortBy(rating(author)).head }).headOption Console.print(somethingToRead.firstPage) Стандартные методы на все случаи жизни:
  • 13.
    Коллекции val somethingToRead = books.filter(isPhysics) .groupBy(_.author).map({ case(author, books) => books.sortBy(rating(author)).head }).headOption Console.print(somethingToRead.map(_.firstPage) .getOrElse("Nothing is there!")) Стандартные методы на все случаи жизни:
  • 14.
    JSON case class Address(street:String, city: String) case class Person(name: String, address: Address) val json = parse("""{ "name": "joe", "address": { "street": "Boulevard", "city": "Helsinki" } }""") assert (json.extract[Person] == Person(joe, Address("Buolevard", "Helsinki")) (Scala 2.10, Json4s)
  • 15.
    XML val xml =<root><tag attr="value">text</tag></root> val xml = XML.loadString("""<root> <tag attr="value">text</tag> </root>""") XPath assert ((xml "tag").text == "text") assert ((xml "tag" "@attr").text == "value")
  • 16.
    Pimp my library! importru.tcs.db.extensions._ val userId = resultSet.getId[User]("user_id") // userId: Id[User] val balance = resultSet.getMoney("balance") // balance: MoneyAmount Берём обычный java.sql.ResultSet Retroactive extension для любого типа!
  • 17.
    Абстракция ▶ Способность находитьи описывать общее, чтобы - экономить время, переиспользуя, или при правках - и недопускать разного поведения и ошибок ▶ Обычно применяются: наследование, параметризация, параметризация типа (generics) ▶ Замыкания и функции над функциями позволяют параметризовать код другим кодом, давая доступ к новому механизму абстракции
  • 18.
    Функции высшего порядка //где-то в определениии Iterable[T]... def filter(predicate: T => Boolean):Iterable[T] def map[A](transform: T => A): Iterable[A] Операции с коллекциями так удобно использовать, как раз потому, что стандартные функции можно параметризировать своим поведеднием
  • 19.
    Примеси/Mixins class Animal trait Philosophical{ def philosophize() { Console.println( "It ain't easy being " + toString) } } class Frog extends Animal with Philosophical { override def toString = "green" } new Frog().philosophize() // It ain't easy being green Помогают выносить общий функцинал без недостатков множественного наследования
  • 20.
    Higher Kinds trait Factory[T]{ def create(): T } Параметр типа первого порядка: trait Functor[H[_]] { def map[A,B](fn: A => B)(fa: H[A]): H[B] } Параметр типа высшего порядка:
  • 21.
    Корректность и тотальность Всёникогда не бывает хорошо "A good programmer is someone who looks both ways before crossing a one-way street." ~ Doug Linder Тотальность — свойство программы, быть определённой для всех входных параметров Scala использует типизацию (type-safety) для проверки корректности при компилляции, где это возможно
  • 22.
    Маленький мотивирующий пример Bookbook = bookShelf.get("Moby-Dick"); Reader reader = readersQueue.peek(); if(reader.favorite.equals(book.author)) { reader.read(book); }
  • 23.
    Маленький мотивирующий пример Bookbook = bookShelf.get("Moby-Dick"); Reader reader = readersQueue.peek(); if(reader.favorite.equals(book.author)) { reader.read(book); } Значения может просто не быть! Если значение необязательно, лучше предусмотреть это в модели типов
  • 24.
    Маленький мотивирующий пример valbook: Option[Book] = bookShelf.get("Moby-Dick") val reader: Option[Reader] = readersQueue.peek if(reader.favorite.equals(book.author)) { reader.read(book) } Теперь программа просто не собралась Разработчик узнал об ошибках сразу, до передачи в тестирование или переноса в бой
  • 25.
    Управление непредвиденным При выполнениивычислений возможно: Возможностью таких исходов можно управлять с помощью типа возвращаемого значения. Такие значения можно создавать и связывать. Тип вместе с операциями называется Монадой. ▶ ничего не получить, — Option ▶ получить исключение, — Try ▶ занимать время, — Future ▶ работать с диском — IO etc.
  • 26.
    Более строгие ограниченияна типы ▶ Параметр типа обязательно должен быть Всегда List[T], но не List ▶ Higher Kinds Не надо переходить к Object ▶ Вариантность Не обязательно переходить к List[_]
  • 27.
    Вариантность case class Invariant[T]() caseclass Covariant[+T]() case class Contravariant[-T]() val in: Invariant[Object] = Invariant[String] val in: Invariant[String] = Invariant[Object] val co: Covariant[Object] = Covariant[String] val co: Covariant[String] = Covariant[Object] val contra: Contravariant[Object] = Contravariant[String] val contra: Contravariant[String] = Contravariant[Object] Scala позволяет указать вариантность для параметров типа:
  • 28.
    Immutability ▶ Возможность изменениявыбирается явно: ▶ Коллекции по умолчанию immutable val immutable = ... var mutable = ... List, Set, Map, ▶ Immutability — невозможность изменить объект или ссылку после создания. есть также mutable версии
  • 29.
    Достоинства Immutability ▶ Меньшеошибок из-за переиспользования переменных для разных целей ▶ Операции проще и однороднее: нет особенностей, связанных с изменением ▶ Просто работать в многопоточной среде, нет расходов на синхронизацию, возникающих из-за concurrent mutable state
  • 30.
    Адаптация к изменениям Когда,проект запущен Клиенты начали пользоваться и писать отзывы Бизнес хочет зарабатывать на продукте А несколько раз в год, запускаются крупные проекты Вам нужно быть очень гибкими!
  • 31.
    Адаптация к изменениям Еслиприходится что-то серьёзно менять, когда меньше кода, лучше видны последствия, а, благодаря типам, изменения ещё и проверяются компиллятором — меньше вероятность сломать. Выше уровень абстракциии и однородный код — нет необходимости влезать в низкоуровневые детали реализации, можно охватить больше случаев меньшими усилиями.
  • 32.
    Группировка запросов и параллельнаяобработка Задача: «Сделать возможность параллельной обработки запросов, когда все запросы обработаны, нужно собрать результаты»
  • 33.
    Группировка запросов и параллельнаяобработка ThreadPool? CyclicBarrier? CountDownLatch? Задача: «Сделать возможность параллельной обработки запросов, когда все запросы обработаны, нужно собрать результаты» Похоже, это сложная задача!
  • 34.
    Группировка запросов и параллельнаяобработка val responses = requests.par.map(processRequest).seq Задача: «Сделать возможность параллельной обработки запросов, когда все запросы обработаны, нужно собрать результаты»
  • 35.
    Нужно собирать информацию сомножества бекендов Продукты CRM Операционные данные Предложения
  • 36.
    Нужно собирать информацию сомножества бекендов Продукты CRM Операционные данные Предложения Очень долго!
  • 37.
    Нужно собирать информацию сомножества бекендов Продукты CRM Операционные данные Предложения Отлично!
  • 38.
    Нужно собирать информацию сомножества бекендов Продукты CRM Операционные данные Предложения Отлично! ThreadPool? CyclicBarrier? CountDownLatch? Похоже это сложная задача! Мне нужно несколько дней!
  • 39.
    Нужно собирать информацию сомножества бекендов Распараллелить долгие вызовы для одного, не самого простого, случая удалось за 30 минут val (operations, offers) = Await.result( future { operationsRepo.readOperations(userId) } zip future { offersRepo.readOffers(userId) }, 60 seconds)
  • 40.
    Расширение команды Новые сотрудникивтягиваются за пару недель: - всё не так сильно отличается, - меньше кода и неявных контрактов почти за два года команда люди менялись в процессе выросла почти с нуля,
  • 41.
    Недостатки ▶ Есть неочевидныеместаx Надо читать документацию и писать тесты ▶ Длинные цепочки вызовов тяжело отлаживать, что заставляет выносить переменные и разделять код на небольшие методы
  • 42.
    Недостатки ▶ Есть неочевидныеместаx Надо читать документацию и писать тесты ▶ Длинные цепочки вызовов тяжело отлаживать, что заставляет выносить переменные и разделять код на небольшие методы Что? тесты и небольшие методы? да ладно!
  • 43.
    Недостатки ▶ Есть неочевидныеместаx Надо читать документацию и писать тесты ▶ Длинные цепочки вызовов тяжело отлаживать, что заставляет выносить переменные и разделять код на небольшие методы ▶ Ограниченная поддержка генерации кода в IDE, впрочем генерировать код не очень актуально ▶ На рынке не так много специалистов по Scala, приходится искать заинтересованных ребят, которые хотят делать что-то новое
  • 44.
    ▶ Та жеплатформа ▶ Те же среды разработки ▶ Та же релизная политика ▶ Доступен код на Java, все библиотеки JVM, старое доброе ООП и императивность мало того, с этого стоит начать Перейти просто администраторам всё знакомо не нужны новые лицензии тестировщики и администраторы в покое если что, всегда есть к чему вернуться,
  • 45.
    Итог ▶ Проще код ▶Меньше ошибок ▶ Меньше времени на типовые вещи ▶ Простая работа с потоками ▶ Просто поддерживать рост ▶ Легко попробовать и перейти
  • 46.
    Итог Scala — отличныйинструмент для успешного проекта! scala-lang.org/downloads
  • 47.
  • 48.