Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scala for successful Internet projects

Scala for successful Internet projects

talk for Russian Internet Tech conference

Vladimir Uspensky

February 11, 2014
Tweet

More Decks by Vladimir Uspensky

Other Decks in Programming

Transcript

  1. О нашем проекте Старт — сентябрь 2011 Запуск — май

    2012 Итого 9 месяцев ▶ Сразу лучший Интернет-банк в России ▶ Хорошие отзывы клиентов ▶ Бешенное развитие в течение года: функционал, безопасность, интерфейс и это не конец...
  2. Общие слова о Scala Один из альтернативных языков на JVM,

    привносит функциональный подход ООП и ФП дополняют друг друга ООП чище чем в Java: все значения — объекты, все операции — вызов метода Функциональный != Процедурный
  3. Book Итак, мы начинаем проект... BookStore sell Author Money accept

    Придумаем модель предметной области: Просто, правда? has
  4. Количество кода, обозримость 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 } Чтобы иметь интерфейсы под рукой, можно объявить всё в одном файле: Всё ещё просто!
  5. Вывод типов 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(); ... }
  6. Вывод типов val booksAuthors = Map("Moby-Dick" -> List("Herman Melville")) for((book,

    authors) <- booksAuthors) { ... } Ничего лишнего!
  7. Коллекции val somethingToRead = books.filter(isPhysics) .groupBy(_.author).map({ case (author, books) =>

    books.sortBy(rating(author)).head }).headOption Console.print(somethingToRead.firstPage) Стандартные методы на все случаи жизни:
  8. Коллекции val somethingToRead = books.filter(isPhysics) .groupBy(_.author).map({ case (author, books) =>

    books.sortBy(rating(author)).head }).headOption Console.print(somethingToRead.firstPage) Стандартные методы на все случаи жизни:
  9. Коллекции 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!")) Стандартные методы на все случаи жизни:
  10. 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)
  11. 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")
  12. 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 для любого типа!
  13. Абстракция ▶ Способность находить и описывать общее, чтобы - экономить

    время, переиспользуя, или при правках - и недопускать разного поведения и ошибок ▶ Обычно применяются: наследование, параметризация, параметризация типа (generics) ▶ Замыкания и функции над функциями позволяют параметризовать код другим кодом, давая доступ к новому механизму абстракции
  14. Функции высшего порядка // где-то в определениии Iterable[T]... def filter(predicate:

    T => Boolean):Iterable[T] def map[A](transform: T => A): Iterable[A] Операции с коллекциями так удобно использовать, как раз потому, что стандартные функции можно параметризировать своим поведеднием
  15. Примеси/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 Помогают выносить общий функцинал без недостатков множественного наследования
  16. Higher Kinds trait Factory[T] { def create(): T } Параметр

    типа первого порядка: trait Functor[H[_]] { def map[A,B](fn: A => B)(fa: H[A]): H[B] } Параметр типа высшего порядка:
  17. Корректность и тотальность Всё никогда не бывает хорошо "A good

    programmer is someone who looks both ways before crossing a one-way street." ~ Doug Linder Тотальность — свойство программы, быть определённой для всех входных параметров Scala использует типизацию (type-safety) для проверки корректности при компилляции, где это возможно
  18. Маленький мотивирующий пример Book book = bookShelf.get("Moby-Dick"); Reader reader =

    readersQueue.peek(); if(reader.favorite.equals(book.author)) { reader.read(book); }
  19. Маленький мотивирующий пример Book book = bookShelf.get("Moby-Dick"); Reader reader =

    readersQueue.peek(); if(reader.favorite.equals(book.author)) { reader.read(book); } Значения может просто не быть! Если значение необязательно, лучше предусмотреть это в модели типов
  20. Маленький мотивирующий пример val book: Option[Book] = bookShelf.get("Moby-Dick") val reader:

    Option[Reader] = readersQueue.peek if(reader.favorite.equals(book.author)) { reader.read(book) } Теперь программа просто не собралась Разработчик узнал об ошибках сразу, до передачи в тестирование или переноса в бой
  21. Управление непредвиденным При выполнении вычислений возможно: Возможностью таких исходов можно

    управлять с помощью типа возвращаемого значения. Такие значения можно создавать и связывать. Тип вместе с операциями называется Монадой. ▶ ничего не получить, — Option ▶ получить исключение, — Try ▶ занимать время, — Future ▶ работать с диском — IO etc.
  22. Более строгие ограничения на типы ▶ Параметр типа обязательно должен

    быть Всегда List[T], но не List ▶ Higher Kinds Не надо переходить к Object ▶ Вариантность Не обязательно переходить к List[_]
  23. Вариантность 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 позволяет указать вариантность для параметров типа:
  24. Immutability ▶ Возможность изменения выбирается явно: ▶ Коллекции по умолчанию

    immutable val immutable = ... var mutable = ... List, Set, Map, ▶ Immutability — невозможность изменить объект или ссылку после создания. есть также mutable версии
  25. Достоинства Immutability ▶ Меньше ошибок из-за переиспользования переменных для разных

    целей ▶ Операции проще и однороднее: нет особенностей, связанных с изменением ▶ Просто работать в многопоточной среде, нет расходов на синхронизацию, возникающих из-за concurrent mutable state
  26. Адаптация к изменениям Когда, проект запущен Клиенты начали пользоваться и

    писать отзывы Бизнес хочет зарабатывать на продукте А несколько раз в год, запускаются крупные проекты Вам нужно быть очень гибкими!
  27. Адаптация к изменениям Если приходится что-то серьёзно менять, когда меньше

    кода, лучше видны последствия, а, благодаря типам, изменения ещё и проверяются компиллятором — меньше вероятность сломать. Выше уровень абстракциии и однородный код — нет необходимости влезать в низкоуровневые детали реализации, можно охватить больше случаев меньшими усилиями.
  28. Группировка запросов и параллельная обработка Задача: «Сделать возможность параллельной обработки

    запросов, когда все запросы обработаны, нужно собрать результаты»
  29. Группировка запросов и параллельная обработка ThreadPool? CyclicBarrier? CountDownLatch? Задача: «Сделать

    возможность параллельной обработки запросов, когда все запросы обработаны, нужно собрать результаты» Похоже, это сложная задача!
  30. Группировка запросов и параллельная обработка val responses = requests.par.map(processRequest).seq Задача:

    «Сделать возможность параллельной обработки запросов, когда все запросы обработаны, нужно собрать результаты»
  31. Нужно собирать информацию со множества бекендов Продукты CRM Операционные данные

    Предложения Отлично! ThreadPool? CyclicBarrier? CountDownLatch? Похоже это сложная задача! Мне нужно несколько дней!
  32. Нужно собирать информацию со множества бекендов Распараллелить долгие вызовы для

    одного, не самого простого, случая удалось за 30 минут val (operations, offers) = Await.result( future { operationsRepo.readOperations(userId) } zip future { offersRepo.readOffers(userId) }, 60 seconds)
  33. Расширение команды Новые сотрудники втягиваются за пару недель: - всё

    не так сильно отличается, - меньше кода и неявных контрактов почти за два года команда люди менялись в процессе выросла почти с нуля,
  34. Недостатки ▶ Есть неочевидные местаx Надо читать документацию и писать

    тесты ▶ Длинные цепочки вызовов тяжело отлаживать, что заставляет выносить переменные и разделять код на небольшие методы
  35. Недостатки ▶ Есть неочевидные местаx Надо читать документацию и писать

    тесты ▶ Длинные цепочки вызовов тяжело отлаживать, что заставляет выносить переменные и разделять код на небольшие методы Что? тесты и небольшие методы? да ладно!
  36. Недостатки ▶ Есть неочевидные местаx Надо читать документацию и писать

    тесты ▶ Длинные цепочки вызовов тяжело отлаживать, что заставляет выносить переменные и разделять код на небольшие методы ▶ Ограниченная поддержка генерации кода в IDE, впрочем генерировать код не очень актуально ▶ На рынке не так много специалистов по Scala, приходится искать заинтересованных ребят, которые хотят делать что-то новое
  37. ▶ Та же платформа ▶ Те же среды разработки ▶

    Та же релизная политика ▶ Доступен код на Java, все библиотеки JVM, старое доброе ООП и императивность мало того, с этого стоит начать Перейти просто администраторам всё знакомо не нужны новые лицензии тестировщики и администраторы в покое если что, всегда есть к чему вернуться,
  38. Итог ▶ Проще код ▶ Меньше ошибок ▶ Меньше времени

    на типовые вещи ▶ Простая работа с потоками ▶ Просто поддерживать рост ▶ Легко попробовать и перейти