Конспект по Scala with Cats

Scala with Cats – это свободно доступная книга, написанная Noel Welsh и Dave Gurnell из Underscore. Охватывает все основы использования библиотеки cats.

Книгу рекомендую читать с выполнением заданий, без этого лично мне полного понимания достичь не удалось.

Ниже краткий насколько возможно конспект по Type Class'ам из книги.

Show

  • вывести на печать любой тип
  • syntax: .show

Eq

  • типобезопасное сравнение
  • syntax:
    • a === b
    • a =!= b

Monoid

  • операции
    • .combine(a1, a2)
    • .empty[A]
  • законы:
    • combine ассоциативный
    • empty должен быть нейтральным элементом (identity element)

Semigroup

  • объединение двух значений
  • только combine из Monoid
  • там где требуется Semigroup, всегда можно использовать Monoid
  • нужен так как не всегда существует нейтральный элемент, пример – NonEmptyList
  • syntax: a |+| b

Functor

  • что-то у чего есть map
  • не стоит думать об этом только, как о проходе по списку, это может быть и работа с внутренним состоянием "контейнера" (в контексте этого "контейнера"), без извлечения значения из "контейнера".
  • операции:
    • .lift(f)
  • syntax:
    • f.map(g)

Contravariant (Functor)

  • из F[B] имея A => B получить F[A]
  • syntax: .contramap(f)

Invariant

  • имея A => B, B => A и Monoid[B] получить Monoid[A]
  • syntax: Monoid[B].imap(f)(g)

Monad

  • 1000 разных подходов к определению, в книге такое: монада – механизм для последовательных вычислений
  • операции
    • .pure(a) – создание монадического контекста из сырого значения
    • .flatMap(f) – извлечение значения из контекста и создание следующего контекста в последовательности
  • любая монада – функтор, map легко построить из pure+flatMap
  • laws:
    • левоассоциативность: pure(a).flatMap(f) == f(a). тут важно помнить об эффектах. именно по этой причине Try не Monad, так как если "снять" с него контекст монады при обычном вызове получим эффект – исключение, а если не снять то получим Failure.
    • правоассоциативность: m.flatMap(pure) == m.
    • ассоциативность: m.flatMap(f).flatMap(g) == m.flatMap(x => f(x).flatMap(g))
  • syntax:
    • .pure[T] (из cats.syntax.applicative)
    • .map(f) (из cats.syntax.functor)
    • .flatMap(f) (из cats.syntax.flatMap)
    • можно использовать for comprehensions из scala
  • при определении своих монад
    • .flatMap
    • .pure
    • .tailRecM – оптимизация для вложенных .flatMap вызовов. метод можно делать @tailrec

Id

  • Identity Monad
  • позволяет использовать в коде, где ожидаются монады немонадные значения
  • мощный инструмент для тестов кода построенного на монадах
  • например
    • 123 : Id[Int]
    • "abc" : Id[String]

Either

  • обычный тип из scala
  • операции:
    • .orElse(Either)
    • .ensure(e)(predicate)
    • .recover(partialFunction)
    • .recoverWith(partialFunction)
    • .leftMap(f)
    • .bimap(f, g)
    • .swap
  • syntax:
    • right bias (flatMap/map/filter работающие на правой части Either, есть из коробки в scala 2.12+)
    • a.asRight
    • a.asLeft

MonadError

  • абстракция поверх Either для работы с ошибками
  • параметризирован типом монады и типом ошибки
  • операции
    • .raiseError(e)
    • .handleError(a)(f)
    • .ensure(a)(e)(predicate)
  • syntax
    • .ensure(e)(predicate)

Eval

  • абстракция над выполнением
  • в частности: позволяет делать стекобезопасные рекурсивные вычисления
  • операции
    • .now(f)
    • .later(f)
    • .always(f)
    • .memoize
    • .defer(=> Eval)

Writer

  • сбор лога при вычислении
  • операции
    • val (log, result) = writer.run
    • .mapWritten(f)
    • .bimap(f, g)
    • .mapBoth( (log, res) => ...)
    • .reset
    • .swap
  • syntax:
    • a.pure[T] (требует Monoid[T] в implicit scope)
    • a.tell
    • a.writer(seq)

Reader

  • построение цепочки операций на основе входных параметров
  • ex: DI, encoders
  • операции:
    • .flatMap – позволяет объединить несколько reader от одного входного значения
    • .map

State

  • передача дополнительного состояния в вычисления
  • моделирование мутабельного состояния в pure functional подходе
  • State[S, A] это S => (S, A)
  • операции
    • val (state, res) = a.run(v).value
    • a.runS(v).value
    • a.runA(v).value
  • стандартные преобразования
    • State.get
    • State.set
    • State.pure
    • State.inspect
    • State.modify
  • синтаксис
    • for comprehension

Monad transformers

  • позволяют комбинировать монады
  • комбинация невозможна без знаний об одной из двух монад
  • например, EitherT позволяет комбинировать любую монаду с Either
  • из коробки: cats.data.{OptionT, EitherT, ReaderT, WriterT, StateT, IdT}
  • соглашение: трансформер определяет внутреннюю монаду, а первый его тип - внешнюю. например, OptionT[List, A] – создаст монаду для List[Option[A]]
  • syntax:
    • если нужно часто определять alias для типов в стеках монад, поможет scala compiler plugin king projector. с ним можно писать так: 123.pure[EitherT[Option, String, ?]]
    • .pure
    • .value
  • использование для реализации:
    • многие монады реализованы через трансформеры
    • type ReaderT[F[_], A, B] = Kleisli[F, A, B]
    • type Writer[W, A] = WriterT[Id, W, A]

Kleisli

  • объединение функций A => F[B], B => F[C] в A => F[C]

Semigroupal

  • объединяет два контекста
  • в литературе иногда называют Monoidal
  • операции:
    • .map2 ... .map22
    • .tuple2 ... .tuple22
    • .contramap2 ... .contramap22
    • .imap2 ... .imap22
  • syntax
    • (a, b, c, ...).tupled
    • (a, b, c ...).mapN
    • (Monoid[A], Monoid[B], ...).imapN(toF)(fromF)
  • Intellij Idea не понимает тип выражения после mapN, это недавно поправили
  • Иногда получаемый результат не сразу очевиден. Например Semigroupal от двух списков будет прямое произведение (каждый с каждым), а не zip списков.

Validated

  • аналог Either, но с накоплением всех ошибок
  • требует Semigroup для типа ошибки
  • операции
    • Validated.valid[E, A](a)
    • Validated.invalid[E, A](e)
    • Validated.catchOnly[Throwable](f)
    • Validated.catchNonFatal(f)
    • Validated.fromTry(a)
    • Validated.fromEither[E, A](a)
    • Validated.fromOption[E, A](a, e)
    • .map(f)
    • .leftMap(f)
    • .bimap(f, g)
    • .toEither
    • .withEither(f)
    • .withValidated(f)
    • .getOrElse(a)
    • .fold(f, g)
  • syntax
    • a.valid[E]
    • a.invalid[A]
    • .pure[Validated[F[E], A]]
    • .raiseError[Validated[F[E], A], E]
    • .tupled

Apply

  • Semigroupal + Functor
  • альтернативный к Semigroupal способ закодировать объединение контекстов
  • операции
    • .ap(f)(a)
    • .product(a, b)

Applicative

  • Apply + pure, который позволяет создать новый Applicative instance из сырого значения
  • Monad = Applicative + FlatMap
  • операции
    • .pure(a)

NonEmpty*

  • аналоги стандартных коллекций, но имеющие строго один или более элементов
  • NonEmptyList
  • NonEmptyVector

Foldable

  • абстракция для foldLeft / foldRight
  • операции
    • .foldLeft(a, i)(f)
    • .foldRight(a, i: Eval[B])(f): Eval[B] (stack safe)
    • привычные методы коллекций, поверх foldLeft: .find, .exists, .forall, .toList, .isEmpty, .nonEmpty ...
    • .combineAll(a) (aka .fold, требует Monoid)
    • .foldMap(a)(f) (требует Monoid)
    • .compose(other)
  • syntax:
    • все операции доступны как синтаксис
    • a.foldLeft(i)(f) и т. д.

Traverse

  • имея набор из F[A], получить F от набора А
  • например, List[Future[A]]Future[List[A]]
  • операции
    • .traverse(a)(f)
    • .sequence(a)
  • syntax:
    • a.traverse(f)
    • a.sequence

Обзоры видео на технические темы, январь-сентябрь 2016

Каждый рабочий день, в обеденный перерыв, у меня есть 15-20 минут на просмотр видео. Читать в этот момент не удобно, это отвлекает от процедуры поглощения пищи, а видео в самый раз.

Впечатления от видео за январь-октябрь этого года в этом посте. В последующем планирую переодически делать такие обзоры.

Предыдущий похожий обзор – Видео со ScalaDays 2015 San Francisco.

Scala Monads: Declutter Your Code With Monadic Design – Dan Rosen

  • общие слова про монады, flatmap и map
  • примеры на scalaz
  • Хороший вывод в конце - монады что-то прячут, заменяя это на типы. Option прячет boilerplate от "if non-null" logic, Validation – try/catch и т.д.

The Multi-threading, Non Blocking IO – Heinz Kabutz

Live coding сессия. Объясняется способ написать сервер: блокирующий, неблокирующий на nio, с тредами или с селекторами. Интересно.

Scalaz: the history, the motivation, the battles, the future – Tony Morris

Не интересно.

Фреймворк Akka и его использование в Яндексе – Вадим Цесько

  • Интересно не сколько про саму akka, там уже некоторые вещи неактуальны для новых версий. Для себя вынес только факт того, что не стоит крутить акторы в системном дефолтном диспечере.
  • Интересно было послушать про систему в общем и узнать, как построена структура обработки данных.
  • Интересно смотреть, как это всё меняется, и меняется ли в связи с akka streams.

CRDTs Illustrated – Arnout Engelen

Хороший начальный обзор CRDT с наглядными схемами.

Propositions as Types – Philip Wadler

  • Известный доклад Вадлера. Стоит посмотреть и посмотреть до конца.
  • Вадлер рассказывает об истории развития теоретической базы под разными концепциями, на которых построены языки программирования.
  • Выжимка: все изобретено дважды. Сначала математиками, потом в computer science.

Learning scalaz – Eugene Yokota

  • Очень поверхностный рассказ про scalaz, скорее про проблемы, которые он может решить.
  • В конце – список полезных книг и их обзор.

Zipkin at Twitter – Jeff Smick

Zipkin – система трассировки логов в распределённых сервисах.

  • Подход интересный, в других системах я такого не видел.
  • Сложилось впечатление, что использовать за пределами твиттер-стека не удобно.

Scaling Intelligence: moving ideas forward – Jessica Kerr

Про кривую изучения scala и community вокруг языка. Про то, как должна выглядеть документация и руководства, чтобы они были понятны людям с разными уровнями понимания computer science и математики, присутствующей в scala.

Позиция автора очень релевантна моему представлению о процессе обучения.

Optimising Scala for fun and profit – Rory Graves

Несколько простых и понятных способов понять, что сделать, чтобы scala работала быстрее. Также про магию, которая лежит за красивыми концепциями в scala, и чего это стоит для производительности.


Видео со ScalaDays 2015 San Francisco

Заметки к некоторым видео с главной scala конференции. Все видео и слайды давно доступны на сайте parleys.com.

Scala - where it came from, where it's going - Martin Odersky

Key note конференции. Советую всем, кто интересуется будущим scala и взглядом на это Мартина. Упоминали новый компилятор dotty, scala.js, scala 2.12. Кстати да, DSL для XML собираются вынести из языка в строковые интерполяторы.

Akka HTTP: the Reactive Web Toolkit - Roland Kuhn

Про стримы в Akka, с демонстрациями кода. Роланд начинает с соединения TCP со стримами, описывает реализации кастомных протоколов и доходит к тому, как на всём этом реализован HTTP. В ответах на вопросы к докладу упомянуто много планов по развитию стримов и HTTP в akka. Из важного я узнал, что netty внутри play планируют заменить на новый HTTP core.

Reactive Slick for Database Programming - Stefan Zeiger

В начале доклада капитанские вещи о том, какие проблемы есть с БД в асинхронных окружениях и как их принято решать. Затем описывается state и IO монады. Затем объяснятся как slick решает вопросы доступа к БД, представления результатов и зачем там Futures. Интересную мысль, которую я вынес, что при текущей моделе БД асинхронные драйвера, которые стали появляться, не решают проблему, так как внизу БД всё равно работает в синхронном стиле.

Type-level Programming in Scala 101 - Joe Barnes

Весёлая вводная в type-level программирование без углубления в незначительные на первых этапах детали. С примерами кода и тестов. Доклад в режиме ответов на вопросы, которые, к сожалению, не всегда слышно.

Function-Passing Style, A New Model for Asynchronous and Distributed Programming - Heather Miller

Интересный доклад о попытках написать решение, которое в некоторых случаях может использоваться как замена модели акторов. В двух словах идея в том, что стейт может жить в одном месте, а обработка его вызываться из другого, для чего по сети можно передавать лямбды, вместо данных. При этом топология решения может быть любая, как ptp, так и master-slave, например. Если я всё правильно уловил, уже есть работающий прототип.


Мониторинг CO2 и работа с USB на scala

Озадачился мониторингом CO2 в помещениях. Сначала смотрел на отдельные датчики, думая собрать на коленке и rPi, но потом нашёл дешёвый сборный комплект Masterkit MT8057. Он настолько отличный, что с удовольствием его рекламирую.

В результате у меня получился драйвер к MT8057 на scala и stand alone утилита, ниже в посте поделюсь некоторыми заметками к этой реализации.

Драйвер MT8057

Для MT8057 есть opensource реализация утилиты для съёма параметров от Олега Булатова написанная на C – co2mon. co2mon вариант хороший и рабочий, поддерживает и OS X, и Linux. Наверное, я бы справился докрутить туда выгрузку данных в InfluxDB, куда я собирался сложить все метрики, но решил пописать своих велосипедов, попрактиковаться в низкоуровневых штуках на scala и вспомнить работу с USB.

В первый подход взял хороший и зрелый проект usb4java, но оказалось, что с поддержкой OS X там беда. Требуются извращения типа выгрузки-загрузки kext'ов, которые у меня ещё и не завелись с первого раза.

Во второй подход взял hid4java, этот продукт хоть и анонсируется, как годный к production использованию, ещё достаточно сырой. Шаг влево, шаг вправо и ловишь jvm crash где-то в кишках интеграции с C-шной библиотекой. Написан через JNA, внутри лежат скомпилированные библиотеки для Linux x86_64, Linux ARM, OS X и ещё нескольких платформ. На OS X работает без всяких танцев с kext'ами. Из проблем – API не очень удобный, хуки вызываются из каких попало тредов, у меня это обходится с помощью обёртки с потокобезопасной очередью. Часть методов работает как бы правильно, возвращает правильные Java объекты, но при попытке что-то вызвать у этого объекта получаешь ошибку или crash, так что пришлось добавить костыль.

HID это отдельный класс USB устройств, поэтому не всегда будет выбор между этими библиотеками, в частности usb4java (а вернее libusb) ничего про HID не знает и знать не собирается.

Scala для работы с байтами

В scala всё просто и удобно. Массивы java типа byte[] в Scala превращаются в Array[Byte]. Вооружившись в добавок scala.collection.JavaConversions можно писать привычный scala код.

Внутри драйвера есть зубодробительный код с распаковкой данных от MT8057 и всё это неплохо легло в scala код, местами даже функциональный.

Результат

На выходе у меня получился драйвер MT8057 и декодер к HID пакетам с данными. В отдельный артефакт я их не выделял, но при необходимости это можно сделать.

Про драйвер можно ничего не знать и использовать готовую утилиту ambient7-mt8057-agent которая из зависимостей требует только jre 1.6+ и пока умеет выводить данные в stdout/stderr (для записи в текстовый лог) или работать в интерактивном режиме с ANSI цветами в консоле. На подходе ещё возможность писать в InfluxDB. Готовый jar можно скачать в github.

Про InfluxDB, если удастся с ним подружиться, расскажу отдельно.


Шаблонизация с handlebars и json4s на scala

Занимаюсь неспешно своим проектом scala.moscow. Одна из задач – примитивный генератор статических сайтов. Очень хотелось чего-то простого для шаблонизации. Думал взять mustache, но оказалось, что это уже не совсем модно-молодёжно, так как есть его расширения handlebars и hogan.

Handlebars мне больше приглянулся. Для scala есть нативная реализация на парсер комбинаторах handlebars.scala и её форк. По фичам scala реализация очень ограничена, в частности, нет встроенной поддержки наследования шаблонов, которая в handlebars делается не силами шаблонизатора, а силами helper'ов. В итоге взял handlebars.java, который выглядит достаточно зрелым и имеет в составе, как минимум, helper'ы partial и block для наследования и i18n.

После выбора шаблонизатора встал вопрос как в него загружать данные. Встроенные резолверы не удобно использовать в scala, да и хотелось чего-то более простого, чем создания пачки классов-контейнеров для данных. А что есть проще для описания примитивных структур данных, чем JSON. Для scala нашёлся отличный DSL для JSON - json4s, у которого в частности есть такой лаконичный DSL:

import org.json4s.JsonDSL._

val data =
    ("copyright" ->
      ("year" -> "2015")
    ) ~
    ("assets" -> ("path" -> "/assets")) ~
    ("title" -> "scala.moscow") ~
      ("main" ->
        ("id" -> "main")
      ) ~
      ("about" ->
        ("id" -> "about") ~
        ("title" -> "scala.moscow :: о проекте")
      )
    )

Плюс к этому handlebars и задумывался для комбинации с JSON, что отражается в его синтаксисе. Плюс к этому сразу решается вопрос, где хранить статические данные, которые не нужно генерить программно – в JSON файлах. В json4s конечно есть не только DSL, но и парсеры на основе нативной scala реализации и jackson.

Чтобы подружить handlebars.java и json4s DSL, написал ValueResolver, идею и часть реализации подсмотрел у handlebars-json4s, но доработал её, решил часть проблем и дописал тестов. Как решу оставшиеся проблемы планирую сделать отдельный артефакт.


Akka. Тестирование в общем и про тестирование кластера в частности

Продолжая заниматься hello world'ом на akka погрузился в вопрос тестирования акторов.

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

Асинхронное vs синхронное тестирование

Есть два подхода синхронное и асинхронное тестирование. Первое в реальной жизни почти никогда не нужно, если только не хочется протестировать какие-то уж совсем внутренние кишки актора. В остальных случаях, лучше тестировать честно, отправляя и принимая ответы от акторов.

TestProbe и TestActor

Часть которую важно понимать. Сначала я думал, что при тестировании будет какая-то чёрная магия, которая позволит мне получать сообщения, летающие между разными акторами.

На деле всё проще. Внутри вашего TestCase создаётся TestProbe и TestActor, которые затем используются для запросов к акторам и анализа приходящих результатов. К сожалению, в документации сразу показывается пример с trait ImplicitSender, который слегка "гримирует" наличие testActor, что вызвало по началу повышенное количество wtf-per-line.

Соответственно набор стандартных assert'ов на самом деле вызывается у стандартного TestProbe. Конечно же таких TestProbe можно даже создать несколько и, например, поместить в них дополнительные специфичные вам assert'ы.

Отсюда же вывод, что для тестирования parent-child взаимодействия придётся вставлять между ними тестовый актор с проксированием сообщений, в документации описаны способы сделать это. В любом случае production код нужно немного к такому подготовить, другой вопрос, что изменения полезны и для других целей.

Cluster testing

С тестированием кластерных конфигурацию всё не так тривиально.

Для начала есть решение multi-jvm тестирования с плагином для sbt, в документации к akka описано как это всё подружить с тестами, чтобы получить Multi Node Testing. Пригодится и для других задач, когда используется просто akka remote.

Печалит, что нужно серьёзно "испортить" конфиг sbt, но, наверное, можно решить выносом таких тестов в отдельный sbt проект. Также ваша IntelliJ IDEA по понятным причинам про такие тесты ничего знать не будет, так как всё магия работает только в связке с sbt. Думаю, в ScalaIDE будет аналогично.

Простого способа дебажить это тоже нет. Логи не очень удобны, так как валяться в параллель со всех JVM. В идеале нужно писать обёртки, которые будут собирать их по каждой ноде отдельно.

"... напоминает мне игру: "Что? Где? Когда?" называется! Непонятно, что где валяется и когда все это кончится!"

Общерекомендуемый подход писать multi-jvm тесты в одном классе, который будет одинаково выполняться на всех нодах. Это обязывает постоянно следить за тем какой код и где исполняется. Например, написанный в лоб assert будет выполнен на всех нодах, часть из которых может быть ещё не присоединена к кластеру.

Постоянно об это спотыкался, но потом написал себе пару удобных утилиток:

import scala.collection.mutable
import org.scalatest.{ BeforeAndAfterAll, Matchers, Suite }
import akka.cluster.Cluster
import akka.cluster.ClusterEvent.{ CurrentClusterState, MemberUp }
import akka.remote.testconductor.RoleName
import akka.remote.testkit.{ MultiNodeSpec, MultiNodeConfig, MultiNodeSpecCallbacks }
import akka.testkit.ImplicitSender



abstract class MultiNodeBaseSpec(config: MultiNodeConfig)
  extends MultiNodeSpec(config)
  with Suite
  with BeforeAndAfterAll
  with MultiNodeSpecCallbacks
  with ImplicitSender
  with Matchers
{

  override def beforeAll() = {
    super.beforeAll()
    multiNodeSpecBeforeAll()
  }

  override def afterAll() = {
    enterBarrier("before-clean-up")
    cleanUp()
    enterBarrier("clean-up")
    multiNodeSpecAfterAll()
    super.afterAll()
  }

  def cluster: Cluster = Cluster(system)
  cluster.subscribe(testActor, classOf[MemberUp])
  expectMsgClass(classOf[CurrentClusterState])
  println(s"myself address: ${node(myself).address}, role: ${myself.name}")

  val currentClusterNodes = mutable.Set[RoleName]()

  def joinToCluster(nodes: Seq[RoleName], seedNode: RoleName): Unit = {
    currentClusterNodes ++= nodes
    // on new nodes await events for all cluster member
    runOn(nodes: _*) {
      cluster join node(seedNode).address
      (receiveN(currentClusterNodes.size).collect { case MemberUp(member) => member.address }.toSet
        should contain theSameElementsAs currentClusterNodes.map(node(_).address).toSet)
    }

    // on existing nodes await events for only new cluster members
    runOn((currentClusterNodes -- nodes.toSet).toList: _*) {
      (receiveN(nodes.size).collect { case MemberUp(member) => member.address }.toSet
        should contain theSameElementsAs nodes.map(node(_).address).toSet)
    }

    enterBarrier("join-"+ nodes.map(_.name).mkString(","))
  }

  def runOnJoinedNodes(a: => Unit): Unit =
    runOn(currentClusterNodes.toList: _*) {
      a
    }

  def cleanUp(): Unit =
    cluster.unsubscribe(testActor)
}

В базовом spec'е выше реализовано:

  • подписывание на события кластера
  • метод joinToCluster для правильного присоединения к кластеру нод
  • метод runOnJoinedNodes для выполнения кода на уже работающих нодах кластера, аналогичный по использованию встроенному runOn

Тестирование сети

Есть встроенная поддержка тестирования транспорта и сети с возможностью эмуляции проблем между нодами (blackhole). При этом я надеюсь как-нибудь попробовать приспособить docker, его API и iptables для данных целей, благо multi-jvm, кажется умеет сам в тестах упаковывать тестовую ноду в jar, раскладывать через ssh+rsync, а затем запускать.

Примеры

Можно глянуть, что получилось у меня. Много полезных примеров я обнаружил в самих исходниках akka и в проекте akka crdt.

Вывод

Тестировать akka, даже в сложных конфигурациях можно и нужно, но tooling ещё требует доработки.


FPConf 2015 - заметки на полях

article featured image

Побывал на конференции FPConf. Поделюсь своими мыслями, возникшими при прослушивании докладов. Я в основном сидел на втором потоке, где было про scala, UI и пр. Первый поток был преимущественно про Erlang и Haskell.

Организаторы обещали, что будет видео, надеюсь пополнить эту статью ссылкам в будущем.

Upd 09.12.2015: дополнил.

Cамурайский путь молодого scala-программиста, Сергей Лобин, Sputnik.ru

В докладе было мало про scala, больше про проблемы при разработки геокодера в Спутнике. Главный печальный для всех фанатов Scala вывод – Scala в production и них не прижилась, найти разработчиков сложно, Go разработчиков найти значительно проще.

Макросы scala, Михаил Муцянко, JetBrains

Хороший вводный доклад про макросы Scala от разработчика Scala plugin в IDEA.

Из запомнившегося:

Удобный способ построения AST в макросе через Q интерполятор (квазикватирование).

Килер фича, когда Михаил нажатием одной волшебной кнопки прямо в IDEA, показал как разворачивается макрос в Scala код. К сожалению оказалось, что это не из production версии плагина и вроде ожидать такое в ближайшее время не стоит, так как работает не всегда корректно. Очень вкусная возможность, будем ждать.

В 2.12 ожидается значительное улучшение в деле разработки макросов за счёт нового проекта ScalaMeta.

Хорошая ссылка на изучение: What Are Macros Good For? от Евгения Бурмако YouTube, слайды. BTW про самого Евгения и его работу над Scala макросами можно узнать в EaxCast S02E10.

Доклад рекомендую к просмотру.

Встраивание языка в строковой интерполятор, Михаил Лиманский, ЭСК

Хороший практический доклад, как вместо DSL порой можно обойтись интерполятором. Какие возможности, плюсы и минусы такого подхода. Можно прямо брать доклад и писать свой интерполятор как по tutorial'у.

В качестве домашнего задания для себя запомнил задачу реализовать интерполятор для типобезопасного форматирования строки. Хотим с aveic попробовать независимо друг от друга реализовать это и посмотреть, что получиться.

Рекомендую для тех, кто интересуется темой.

Lenses And Prisms, Эдвард Кметт

Самый звёздный докладчик на конференции. К сожалению без знания хотя бы основ синтаксиса Haskell понять что-то было сложно. Докладчик проводил доклад в потрясающей форме - выдавая как из пулемёта кучу кода на Haskell прями в vim'е.

Чтобы понять что к чему, я смотрел на порт этой библиотеки на Scala - Monocle.

В общих словах – это удобный способ изменения иммутабельных структур с возможностью написания максимально абстрактных маленьких кусочков с дальнейшим комбинированием в более сложные кусочки.

Видео к сожалению не выложили.

Реактивные потоки в backend-е, Алексей Романчук, 2ГИС

Хорошая success story про использование akka streams в production. Для меня теперь это один из ответов на вопрос «где нам нужна akka?».

Забавные слайды с цветными "сосиками".

Прозвучавшее число потерь на akka streams по сравнению с голыми акторами ~10%.

Рекомендую всем, кому интересны akka streams.

Scala performance для сомневающихся, Роман Гребенников, Sociohub.ru

Самый запомнившийся доклад. Покрывает сразу две темы - пару примеров того как все модные молодёжные FP штуки из scala отражаются в bytecode JVM и машинном коде, какие потери они за собой несут или не несут. А также способы самому развернуть, померить и понять как выполняется тот или иной код.

Вкратце про проблемы – простой pattern matching по типу параметра - также быстро как колбаса из if'ов. Есть проблемы с boxing/unboxing в Scala collection и прикладывать @specialized не поможет.

Рекомендую к просмотру.

В общем

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

Не посмотрел в живую «Фронтэнд без грусти» Никиты Прокопова, о котором наслышан от коллег, которые видели доклад на какой-то другой конференции. А также хочется поглядеть на «Aрхитектура UI на основе функциональных линз» от Ильи Беда.

Буду ждать видео. Дождались.


Akka graceful shutdown и обработка системных сигналов

Когда начал писать для себя hello world на akka сразу захотелось сделать какой-то артефакт, удобный для жизни в дикой среде production. Хочется, чтобы приложение вело себя как примерный гражданин, умело правильно реагировать на всякие системные сигналы и прочее.

С упаковкой приложения в jar вместе с scala runtime удалось легко разобраться. Помогут sbt-native-packager и sbt-assembly. Напишу про это позже, когда ещё пойму как правильно результат сложить в .deb пакет.

Не сразу почему-то удалось сделать правильные реагирования на сигналы системы. Меня пока не интересуют всякие специфичные штуки, достаточно было двух сигналов выключения - SIGINT и SIGTERM, по которым хотелось аккуратно потушить ноду в akka cluster - убить её локальные акторы, отсоединиться и пр.

Сначала попробовал использовать модный_молодёжный метод scala.sys.addShutdownHook. Он принимает => Unit и выполняет его в отдельном треде, когда завершается jvm. Вызов не гарантируется, что в данном случае ок. Плюс метода - переносимость. Минус - код возврата не возможно поставить, метод System.exit() выполняется, но код не ставиться. Экспериментально понял, что на SIGINT код всегда 130, на SIGTERM - 146. Вторая проблема - akka при выключении часть логов пишет в stdout, которые при использовании хука уже не выводятся. Не проверял, но кажется stdout закрывается раньше, чем вызывается этот хук.

Пришлось взять в руки обработчик сигналов и делать на нём. Он из пакета sun.misc, так что с альтернативными jvm могут быть вопросы, но меня это не волнует в данный момент.

В итоге метод выглядит примерно так:

import java.util.concurrent.atomic.AtomicBoolean
import sun.misc.SignalHandler
import sun.misc.Signal
import scala.concurrent.duration._

import com.typesafe.config.ConfigFactory
import akka.cluster.Cluster
import akka.actor.ActorSystem
import akka.util.Timeout

object Main extends App with SignalHandler {

  val SIGING = "INT"
  val SIGTERM = "TERM"

  val terminated = new AtomicBoolean(false)

  // регистрируем сам App объект, как обработчик сигналов
  Signal.handle(new Signal(SIGING), this)
  Signal.handle(new Signal(SIGTERM), this)

  val config = ConfigFactory.load()
  val system = ActorSystem("main", config)
  implicit val executionContext = system.dispatcher
  implicit val defaultTimeout = Timeout(500.millis)
  val cluster = Cluster(system)

  //hook для akka, будет использоваться для любых остановок, не только по сигналам
  system.registerOnTermination {
    System.exit(0)
  }

  // собственно обработчик
  override def handle(signal: Signal): Unit = {
    if (terminated.compareAndSet(false, true) && List(SIGING, SIGTERM).contains(signal.getName)) {
        system.shutdown()
    }
  }
}

Теперь и сигналы честно обрабатываются, и коды возвратов правильные, и все выходы из приложения сводятся в хук для akka.


Akka для распределённых приложений - remote vs cluster

Буду делать небольшие заметки на полях по результатам изучения akka.

Akka – это система акторов на jvm, реализованная на scala. Можно использовать и из java. Про саму концепцию акторов можно почитать в интернетах, здесь я буду останавливаться на особенностях akka.

Что читать?

Я пока рекомендую такие источники:

  • официальная документация - она хорошая, содержит как теоретические основы, так и конкретные примеры кода и описания API. Можно читать из начала в конец, как книгу. Есть pdf.

  • "Akka in Action" by Raymond Roestenburg, Rob Bakker, and Rob Williams - неплохая книга. Я читаю 16 MEAP (early access), к осени-зиме 2015 обещают релиз. Цена 40$. В основном ориентируюсь на документацию, но в книге плюс - итеративный подход к реализации демо проекта. Начинают с hello world'ов и далее по нарастающей. Было полезно, когда хотелось быстрого старта без изучения всех тонкостей теории.

  • Stack Overflow - как всегда стоит делать аккуратно. Фреймворк активно развивается, некоторые вещи появляются, которые раньше нужно было самому велосипедить, некоторые становятся deprecated и пр., но есть не мало полезных объяснений.

  • Googling по тонким вопросам и непоняткам, который нередко приводит в google группу akka.

Если жалко 40$, то книгу можно не покупать. Благородя Typesafe хайпа в сети по akka хватает. Для фреймворка – это хорошо.

Применимость

Меня akka интересует в основном с точки зрения распределённых систем. Однако akka работает и на одной jvm. Даже есть успешные кейсы, когда в такой подход даёт выигрыш в противовес обычным многопоточным потокам.

При этом akka имеет грамотный подход – реализации работы различных механизмов по сети ставиться во главу угла, а локальная работа рассматривается, как один из способов оптимизации.

remote vs cluster

В akka есть понятие "akka remote" – это возможность работать с любыми акторами прозрачно, не зная, где именно они находятся - локально или удалённо. Вы должны каким-либо образом знать, как до этих акторов добраться и эта реализация на вашей ответственности.

Поверх akka remote строиться akka cluster, который реализует кластерное решение, т.е. количество нод может динамически меняться, каждой ноде может быть назначена роль, которую она исполняет в системе.

Ноды договариваются с друг другом по gossip протоколу и внутри используют для хранения состояния CRDT структуры. Одна из нод назначается мастером, если она погасла мастером станет автоматически другая нода.

Гашение нод вы определяете сами, система только говорит, что нода стала не доступна, вы сами можете решить, что делать - удалять её из кластера или нет. Есть стандартная реализация, когда нода удаляется после недоступности в течении какого-то времени.

На события кластера, или более точно на жизненный цикл его нод, можно подписаться.

Некоторые вещи в akka cluster ещё только в планах на реализацию.

Нет встроенного механизма сказать кластеру "я запустился", у ноды при старте должен быть список seed нод, хотя бы одна из которых должна работать. Seed нодой может быть любая нода работающего кластера. Т.е. в реальной жизни тут нужно будет брать какие-то решения для discovery. Как вариант предлагают также держать ноды, которые ничего не делают (не запускают пользовательские акторы), а только используются как seed ноды.

Пока всё. Некоторые из аспектов описанных выше я буду изучать подробнее и ещё про них напишу. Если найдёте в моих записях ошибки, буду раз замечаниям.


Отличие Future от Promise

Недавно пытался на пальцах объяснить отличие Future от Promise. Понятия эти очень близкие, даже статья на Wikipedia про них одна.

Как правило Future и Promise используются как контейнер, для представления данных, которых пока нет, но уже можно сформировать цепочку их обработки. Реализации, которые мне известны: Deffered в Twisted, Future в python 3, его примитивный backport в tornado и Deffered в jQuery.

Мне нравится такое определение разницы:

Future – это контейнер, на результат которого вы подписываетесь, но не управляете его появлением.

Promise – это контейнер, результат в который вы предоставляете и сами управляете его появлением в будущем.

Условно можно считать Future "переменной", а Promise "функцией по установке переменной". Логично, что во многих реализациях эти объекты порождаются парами. Иногда даже эти понятия смешивают в один контейнер, который предоставляет реализации обоих задач, например, так сделано в jQuery и python. Но даже, если реализация смешана, полезно каким-либо образом, хотя бы в соглашениях понимать имеете ли вы право "зажечь" контейнер или имеете право только подписываться на него.

За Future не нужно следить и можно считать, что код который их вернул обязательно когда-нибудь вызовет ваши callback'и. За Promise же вы ответственны и должны не забыть вызвать его в любом случае, даже если ваш код упал с какой-то ожидаемой или не ожидаемой ошибкой.


Использование ansible в Docker контейнерах

Для решения задачи настройки сервера уже давно придумали много вещей лучше, чем просто bash скрипт. Я в частности, использую ansible. Но для создания docker контейнеров по прежнему предлагается механизм который не далеко ушёл от bash скриптов.

Мне понадобилось строить на сервере набор контейнеров, причём полностью автоматизировано. Если создание docker images на основе Dockerfile и работа с готовыми контейнерами в ansible решается из коробки, то с настройкой контейнеров через ansible пришлось немного извратиться.

Идея простая:

  • сделать базовый image в docker в котором через Dockerfile устанавливается только ansible,
  • засинхронизировать с локальной машины на хост машину все файлы, нужные для ansible - роли, настройки и таски,
  • создать контейнер,
  • засинхронизированные настройки подключить в volume внутри контейнера,
  • запустить ansible-playbook через docker exec,
  • готовые контейнеры при необходимости коммитяться и используются как основа для других контейнеров.

Плюсы ansible здесь – идемпотентность, готовые модули и возможность использовать некоторые роли, как для хост машин, так и для контейнеров. Например, у меня есть абстрактная роль nginx и отдельная роль поверх неё nginx_container со спецификой для контейнеров.

То что получилось и подробности работы можно посмотреть в репозитории scala-moscow/deploy.


Питон в коробке – venv в python 3.3

Наверняка, большинство из тех, кто разрабатывает или деплоит Python приложения, использует виртуальные окружения. В частности через virtualenv, написанный Ian Bicking.

Идея оказалась так хороша и распространена, что нечто похожее теперь присутствует в Python 3.3 из коробки в виде модуля venv. Он почти такой же, как virtualenv, только немного лучше.

Полная версия поста на Хабрахабр


Ваш заказ -555 тазиков на сумму -55500 руб

Забавный пост написанный на Хабре в 2007 году. В 2007, Карл!

Речь идёт о распространённой ошибке в интернет-магазинах. В offline-магазине для выбора 555-и синих пластиковых тазика вам придётся: положить в корзину все 14 штук с витрины, залезть на верхнюю полку стеллажа и достать ещё 43, а за остальными идти с продавцом на склад и найти там ещё лишь 143 штуки.

Полная версия поста на Хабрахабр