@okapiesさんによるReactiveとは何か?

今週の社内勉強会では@okapiesさんにお越しいただいてReactiveについてお話ししていただきました。

f:id:takezoe:20151016122757j:plain

先日TISさんで開催されたReactive System Meetup in 西新宿での講演の再演という形でお願いさせていただいたのですが、弊社のメンバーもReactive System Meetupに参加していた者がいたのですが、今回は時間が長かったこともあり、そのときよりも丁寧にご説明いただきわかりやすかったとのことでした。

Reactiveという単語自体かなり広範囲で使われており、経緯やコンテキストによって様々な捉え方をされていると思うのですが、今回の講演では「Reactiveの3つの立場」として以下の3つに切り分けて順に説明されていました。

  • プログラミングモデル(Reactive Programming)
  • ランタイム(Reactive Streams)
  • システムアーキテクチャ(Reactive Systems)

個人的にはランタイムについてはReactive Streamsそのものというよりは、用途や課題に応じたランタイムの多様性が必要だと感じています。システムアーキテクチャの側面から見てもReactive Streamsはランタイムというよりはコネクタの仕様なので、どういったミドルウェアやサービスがReactive Streamsに対応してくるか?という部分が重要になってくるのではないでしょうか。

また、勉強会の後には弊社のメンバー数名と雑談をさせていただいたのですが、実際はストリームよりも先にIN/OUTが詰まることが多いのでその部分がReactive Streamsに対応してくれないと厳しい、監視機能つきのエージェント的なものでバックプレッシャーを発生させられるとよいのでは?といった話も出ていました。(人力バックプレッシャーつらいなんて話もw)

フロントエンドからシステムアーキテクチャまで様々なコンテキストで使われる「Reactive」というキーワードですが、@okapiesさんのお話しで知識を整理できたのではないかと思います。@okapiesさん、ありがとうございました!

なお、@okapiesさんも準備委員に参加されているScalaMatsuri 2016では登壇者や準備委員会のスタッフを募集しているとのことなので興味のある方は是非ご応募を!

quicklensでネストしたケースクラスを簡単にコピーする

Scalaのケースクラスはイミュータブルなため、値を変更する場合はcopy()メソッドで一部のプロパティを書き換えた新しいインスタンスを生成する必要があります。

しかし、ネストしたケースクラスの場合、copy()メソッドの呼び出しもネストさせる必要があり、かなり冗長なコードになってしまいます。

case class Street(name: String)
case class Address(street: Street)
case class Person(address: Address, age: Int)

val person = Person(Address(Street("1 Functional Rd.")), 35)

// ネストしたプロパティを変更したインスタンスを生成
val p2 = person.copy(
  address = person.address.copy(
    street = person.address.street.copy(
      name = "3 OO Ln."
    )
  )
)

こんな場合はLensを使うと簡単にコピーすることができます。たとえばMonocleというLensライブラリだと以下のような感じになります。

val p2 = (_address ^|-> _street ^|-> _name).modify(_ =>"3 OO Ln.")(person)

LensはScalazやShapelessといったScalaの関数型ライブラリにも含まれていますが、このようなコードを書くためにはケースクラスに対応したLensを自前で定義しなくてはなりません(Monocleの場合はアノテーションやマクロで自動生成することもできますが)。そこで登場するのがquicklensというライブラリです。

github.com

このライブラリはimport文を1行追加するだけで、以下のような感じでケースクラスのコピーを行うコードを簡単に書くことができるようになります。

import com.softwaremill.quicklens._

val p2 = person.modify(_.address.street.name).setTo("3 OO Ln.")

元の値を加工したい場合はsetTo()メソッドの代わりにusing()メソッドを使います。また、modify()メソッドはチェーンさせることもできます。

// 元の値を加工したい場合はusingを使う
val p3 = person.modify(_.address.street.name).using(_.toUpperCase)

// modifyをチェーンさせて複数のプロパティを変更
val p4 = person
  .modify(_.address.street.name).using(_.toUpperCase)
  .modify(_.age).using(_ - 1)

他のLensライブラリと比べると非常に手軽に使えるので便利なのですが、実はこのquicklensはLensを生成しているわけではなく、modify()メソッドの呼び出しをマクロでcopy()メソッドを呼び出すコードに展開しているだけです。コードも割とシンプルなのでマクロの参考としてもよいのではないかと思います。