registry and reference パターン

ここ最近考えている「パターン」があって、オブジェクトを永続化/シリアライゼーションするみたいな状況で出てくる。

問題提起として、以下のような case class を考えてみてほしい:

scala> case class User(name: String, parents: List[User])
defined class User
 
scala> val alice = User("Alice", Nil)
alice: User = User(Alice,List())
 
scala> val bob = User("Bob", alice :: Nil)
bob: User = User(Bob,List(User(Alice,List())))
 
scala> val charles = User("Charles", bob :: Nil)
charles: User = User(Charles,List(User(Bob,List(User(Alice,List())))))
 
scala> val users = List(alice, bob, charles)
users: List[User] = List(User(Alice,List()), User(Bob,List(User(Alice,List()))),
  User(Charles,List(User(Bob,List(User(Alice,List()))))))

注目してほしいのは parents という他のユーザを参照するリストを保持してることだ。
次に、users リストを JSON に変換したいとする。

[{ "name": "Alice", "parents": [] },
{ "name": "Bob",
  "parents": [{ "name": "Alice", "parents": [] }] },
{ "name": "Charles",
  "parents": [{ "name": "Bob", "parents": [{ "name": "Alice", "parents": [] }] }] }]

sjson-new とアズカバンの囚人

in

本稿は sjson-new に関する第3部だ。パート1パート2も是非読んでみてほしい。

sbt のコード内にはデータ永続化が数百メガバイトのオーダーに達している部分がいくつかあって、特にマシンに SSD が積まれていない場合は性能ボトルネックになる疑いがあるんじゃないかと思っている。
当然、最初に飛びついたのは Google Protocol Buffers のエンコーディングを参考に独自のバイナリフォーマットを実装することだった。

sbt-jmh を用いたマイクロベンチマーク

僕がまずやるべきだったのは、ベンチマークを取ることだ。@ktosopl (Konrad Malawski)君の sbt-jmh を使うとマイクロベンチマークは簡単に作ることができる。ビルドにプラグインを入れて、JmhPlugin を有効化したサブプロジェクトを定義するだけだ。

sjson-new と LList を用いたカスタムコーデック

in

2ヶ月ぐらい前に sjson-new について書いた。週末にまたちょっといじってみたので、ここに報告する。
前回は Scala エコシステムにおける JSON ライブラリの家系をたどって、複数バックエンドに対応し、かつ型クラスベースの JSON コーデックライブラリという概念を導入した。課題は、カスタムコーデックを簡単に定義できるようにする必要があるということだった。

私家版 shapeless

4月に書いたのと先週までの間に flatMap(Oslo) 2016Scala Days New York 2016 という 2つのカンファレンスがあった。残念ながら、僕は flatMap の方には行けなかったけども、Daniel Spiewak さんの "Roll Your Own Shapeless" (「私家版 Shapeless のすゝめ」) というトークを New York で聞けた。flatMap 版の方が完全版でそれは vimeo にも出てるので、是非チェックしてみてほしい。

sbt の内部では、sbinary を用いたキャッシングに HList が用いられてたりする:

implicit def mavenCacheToHL = (m: MavenCache) => m.name :*: m.rootFile.getAbsolutePath :*: HNil
implicit def mavenRToHL = (m: MavenRepository) => m.name :*: m.root :*: HNil
...

そういう影響もあって、HList とか Shapeless の LabelledGeneric みたいなのがあれば JSON object を表す中間値としていいのではないかと思っていたので、Daniel のトークには最後に背中を押してもらった気がする。

本稿では、HList の目的を特化した LList というものを紹介する。

LList

sjson-new には LList というデータ型があって、これは labelled heterogeneous list、ラベル付された多型リストだ。
標準ライブラリについてくる List[A] は、A という同じ型しか格納することができない。標準の List[A] と違って、LList はセルごとに異なる型の値を格納でき、またラベルも格納することができる。このため、LList はそれぞれ独自の型を持つ。REPL で見てみよう:

scala> import sjsonnew._, LList.:*:
import sjsonnew._
import LList.$colon$plus$colon
 
scala> import BasicJsonProtocol._
import BasicJsonProtocol._
 
scala> val x = ("name", "A") :*: ("value", 1) :*: LNil
x: sjsonnew.LList.:*:[String,sjsonnew.LList.:*:[Int,sjsonnew.LNil]] = (name, A) :*: (value, 1) :*: LNil
 
scala> val y: String :*: Int :*: LNil = x
y: sjsonnew.LList.:*:[String,sjsonnew.LList.:*:[Int,sjsonnew.LNil]] = (name, A) :*: (value, 1) :*: LNil

x の長い型の名前の中に StringInt が書かれているのが分かるだろうか。y の例が示すように、String :*: Int :*: LNil は同じ型の略記法だ。

BasicJsonProtocol は全ての LList の値を JSON オブジェクトに変換することができる。

foundweekends

週末に趣味プログラミングをする人のための Github organization として foundweekends を作った。参加したい人は twitterGitter で声をかけてください。

当面の活動は @n8han から conscriptgiter8pamflet を引き継ぐことだ。

sjson-new

in

Scala プログラマの週末の嗜みとして、僕も一つ sjson-new という JSON ライブラリを書いてみた。
sjson-new は型クラスベースの JSON コーデックライブラリで、Jawn のための wit だ。つまり、複数のバックエンドに対して sjson的なコーデックを提供することを目指している。

コードは spray-json を元にしているが、データの扱いに関しての考え方は Scala Pickling の方に近い。Pickling と違って、sjson-new-core はマクロとか普通のパターンマッチング以外での実行時リフレクションなどは一切使っていない。

Json4s-AST と使う場合:

libraryDependencies += "com.eed3si9n" %%  "sjson-new-json4s" % "0.1.0"

Spray と使う場合:

libraryDependencies += "com.eed3si9n" %%  "sjson-new-spray" % "0.1.0"

sbt server リブート

in

これは先日書いた sbt 1.0 ロードマップの続編だ。この記事では sbt server の新しい実装を紹介する。感想やコメントがあれば sbt-dev mailing list にお願いします。

sbt server の動機は IDE との統合の改善だ。

ビルドは、巨大で、可変で、共有された、状態のデバイスだ。ディスクのことだよ! ビルドはディスク上で動作するのもであって、ディスクから逃れることはできない。

-- Josh Suereth、The road to sbt 1.0 is paved with server より

マシンに積んであるディスクは根本的にステートフルなものであり、sbt がタスクを並行実行できるのもそれが作用に関する完全なコントロールを持っていることが大前提になっている。同じビルドに対して sbt と IDE を同時に実行していたり、複数の sbt インスタンスを実行している場合は、sbt はビルドの状態に関して一切保証することができない。

sbt server というコンセプトは元々 2013年に提案された。同時期にその考えの実装として sbt-remote-control プロジェクトも始まった。ある時点で sbt 0.13 が安定化して、代わりに Activator が sbt-remote-control を牽引する役目になり、sbt 本体を変えない、JavaScript をクライアントとしてサポートするなどという追加の制約を課せられることになった。

sbt 1.0 を念頭に置いて、僕は sbt server の取り組みをリブートすることにした。sbt の外で何かを作るのではなくて、僕はアンダーエンジニアリングすることを目指している。つまり「オーバーエンジニアリング」の逆で、自動ディスカバリーや自動シリアライゼーションといった僕から見て本質的じゃない既存の前提を一度捨てようと思っている。代わりに、気楽に sbt/sbt コードベースに取り込めるような小さいものがほしい。Lightbend 社は Engineering Meeting といってエンジニア全員が日常から離れた所に集結して議論をしたり、内部でのハッカソン的なことをやる合宿みたいなことを年に数回やっている。2月に美しいブダペストで行われたハッカソンでは sbt server リブートという提案に Johan Andrén (@apnylle)、 Toni Cunei、 Martin Duhem の 3人が乗ってくれた。目標として設置したのは、IntelliJ IDEA に sbt のビルドを実行させるボタンを付けることだ。

sbt 1.0 ロードマップ

sbt 1.0 にに関して TL上とかで議論があったので、叩き台としてこれを書くことにした。何かをちゃんとリリースできるように仕切り直しするための中期的なミッション・ステートメントだと思ってほしい。sbt-dev mailing list にて今後も議論を続けていきたい。

タイミング

いつ sbt 1.0 をリリースできるかという予定はまだ見当が付いていない。
sbt 1.0 の最大の機能はコードの再組織で、それは既に進んでいる: http://www.scala-sbt.org/0.13/docs/Modularization.html

sbt/io、 sbt/util、 sbt/librarymanagement、 sbt/incrementalcompiler といったモジュールがある。実装という観点からするとインクリメンタルコンパイラが sbt の中で一番複雑な部分だと思うので、まずはそれをモジュール化することを目標としてきた。全部のモジュールの API が安定したときが、sbt 本体にも 1.0 を付けれる時になる。

モジュール化の動機

ライフスタイルとしての ScalaMatsuri

in

僕にとって ScalaMatsuri とはライフスタイルのようなものだ (他の 27名近くいるオーガナイザーも同じように思っているんじゃないだろうか)。確かに、近日東京で 550名を動員したカンファレンスがあって、それは成功に終わった。しかし、オーガナイザーは 2015年の 2/28 からかれこれ 11ヶ月間準備してきた。僕が関わったのは一部でしかないけども、ScalaMatsuri 2016 はこれまでで一番関わりの深いカンファレンスになったと思う。この何ヶ月の間 Slack、Hangout たまには対面で、さまざまな議論を重ねてきた。面白かったのは、一緒にアイディアを出し合ってそれが実現されるのをみることだったと思う。僕が奇抜なアイディアを出して、その詳細を誰かが実行してくれることもあれば、他の人が始めたことを元に僕が現場の作業をすることもあった。

色々言いたいことは「グローバルな技術カンファレンス」と「日本のコミュニティの交流」の両立でも書いちゃったので、かぶる部分もあると思う。

-Yno-lub を用いた Scala の厳格化

in

Scala は柔軟なプログラミング言語なので、個人的な Good Parts のような言語のサブセット、もしくは主義主張のあるスタイルガイドを作ることは有用だ。

セットアップ

-Yno-lub を試してみたい人は、以下を project/ynolub.sbt に書いて sbt プラグインを引っ張ってくる:

addSbtPlugin("com.eed3si9n" % "sbt-ynolub" % "0.2.0")

lub

Scala の型推論が型 A と型 B を統合するとき、それらの <:< に関する lub (least upper bounds, 最小上界) を計算する。この過程を lubbing と呼ぶこともある。具体例で説明する:

scala> if (true) Some(1) else None
res0: Option[Int] = Some(1)
 
scala> if (true) List(1) else Nil
res1: List[Int] = List(1)

ここ数年考えているのは、少なくとも今ある形での lubbing は有益ではないのではないか、ということだ。

Syndicate content