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

Post on 25-Jan-2015

419 views 2 download

description

 

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

Язык программирования 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!"))

Стандартные методы на все случаи жизни:

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

XMLval xml = <root><tag attr="value">text</tag></root>

val xml = XML.loadString("""<root> <tag attr="value">text</tag> </root>""")

XPathassert ((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]

Операции с коллекциями так удобно использовать, как раз потому, что стандартные функции можно параметризировать своим поведеднием

Примеси/Mixinsclass 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.ruskype: tcs_uspenskiy