猫番: 16日目

猫番: 16日目書いた。

sbt 0.13.15 リリースノート

in

互換性に影響のある新機能、変更点、バグ修正

  • sbt 0.13.15 は Maven のバージョンレンジをできる限り取り除く。詳細は後ほど。

改善点

  • 予備として JDK 9 との互換性を追加した。この機能は 0.13.15 以降のネイティブパッケージを必要とする。 #2951/143 by @retronym
  • オフライン・インストール用に "local-preloaded" レポジトリを追加する。詳細は後ほど。
  • ウォーミングアップされた JVM に留まるように、バッチモードで sbt を実行すると [ENTER] を押して shell に切り替えるよう通知するようにした。 #2987/#2996 by @dwijnand
  • .taskValue を使わずに sourceGenerators += Def.task { ... } と書けるようにするために Append のインスタンスを追加した。 #2943 by @eed3si9n
  • JUnitXmlTestsListener が生成する XML が無視、スキップ、保留状態のテストにそれぞれフラグを立てるようにした。 #2198/#2854 by @ashleymercer
  • プロジェクトが Dotty を使ってコンパイルしていると検知した場合に、自動的に scalaCompilerBridgeSource を設定して、Dotty プロジェクトのボイラープレートを軽減するようにした。ただし、sbt における Dotty サポートは現在実験的であり、正式にはサポートされていないことに注意。詳細は dotty.epfl.ch 参照。 #2902 by @smarter
  • sbt new のレファレンス実装である Giter8 を 0.7.2 にアップデートした。

Contraband、case class の代替案

in

しばらく考えている疑問がいくつかある:

  • データや API はどう書かれるべきだろうか?
  • そのデータは Java や Scala ではどう表現されるべきか?
  • そのデータは JSON などのワイヤーフォーマットにどう変換することができるか?
  • そのデータをどうやってバイナリ互換性を崩さずに進化させることができるか?

case class の限界

Scala でデータ型を表現する慣用的な方法は sealed trait と case class だが、バイナリ互換性を保ったままフィールドを追加することができない。簡単な Greeting という case class を例に取って、それがどのようなクラスとコンパニオンオブジェクトに展開されるか考察してみよう:

package com.example
 
class Greeting(name: String) {
  override def equals(o: Any): Boolean = ???
  override def hashCode: Int = ???
  override def toString: String = ???
  def copy(name: String = name): Greeting = ???
}

sbt 0.13.x、sbt サーバー、sbt 1.0 の動向

同僚の Dale Wijnand (@dwijnand) と一緒に ScalaSphere でトークをしてきた。The state of sbt 0.13, sbt server, and sbt 1.0 (ScalaSphere ver):

Gigahorse 0.2.0

in

Gigahorse 0.2.0 をリリースした。新機能は 2つのバックエンドを選べるようになったことだ。
@alexdupre さんが AHC 1.9 から Netty 4 ベースの AHC 2.0 への移行をコントリビュートしてくれた。#12

さらに、#15 で僕が実験的な Akka HTTP サポートを追加した。

詳しくは Gigahorse ドキュメントを参照してほしい。

Scala 2.12.0 リリースノート

in

昨日リリースされたばかりの Scala 2.12.0 のリリースノートを翻訳しました。
Lightbend 社 Scala チームのコンパイラ魂を感じ取れる、マニアな内容になっています。

Scala 2.12.0 がリリースされました!

Scala 2.12 コンパイラは Java 8 から使えるようになった新しい VM 機能を利用するために、完全なオーバーホールが行われた。

gigahorse-github 0.1.0

gigahorse-github 0.1.0 をリリースした。これは、Github API v3 のための Gigahorse プラグインだ。

レポジトリ情報を取得する使用例はこんな感じだ:

scala> import gigahorse._, gigahorse.github.Github, scala.concurrent._, duration._
 
scala> val client = Github.localConfigClient
client: gigahorse.github.LocalConfigClient = LocalConfigClient(OAuthClient(****, List(StringMediaType(application/json), GithubMediaType(Some(v3),None,Some(json)))))
 
scala> Gigahorse.withHttp { http =>
         val f = http.run(client(Github.repo("eed3si9n", "gigahorse-github")), Github.asRepo)
         Await.result(f, 2.minutes)
       }
res0: gigahorse.github.response.Repo = Repo(https://api.github.com/repos/eed3si9n/gigahorse-github, gigahorse-github, 64614221, User(https://api.github.com/users/eed3si9n, eed3si9n, 184683, Some(https://github.com/eed3si9n), Some(https://avatars.githubusercontent.com/u/184683?v=3), Some(), Some(User), Some(true), None, None), eed3si9n/gigahorse-github, Some(Gigahorse plugin for Github API v3),...

gigahorse-github 本体に興味がある人は、README にドキュメンテーションがあるのでそちらを参照してほしい。

Gigahorse を拡張する

Gigahorse プラグインの書き方を解説したGigahorse を拡張するというページも書いた。
Dispatch プラグインの書き方とだいたい同じになっている。そこで書いたように、JSON データバインディングをスキーマから自動生成するという方法をとっている。

そのため、僕にとって gigahorse-github は Gigahorse の概念実証であると同じかそれ以上に sbt-datatype の概念実証であるという意味合いがある。やはりというか、全コンポーネントで細かいバグが出てきたので有益な演習だったといえる。

Gigahorse 0.1.0

更新: Gigahorse 0.1.1 を使ってください。

Gigahorse 0.1.0 をリリースした。これは Scala のための HTTP クライアントで、内部にAsync Http Client を使っている。詳しくは Gigahorse ドキュメントを書いたので、それを参照してほしい。ライブラリがどういう感じなのかを例でみるとこんな感じだ。

scala> import gigahorse._
scala> import scala.concurrent._, duration._
scala> Gigahorse.withHttp(Gigahorse.config) { http =>
         val r = Gigahorse.url("http://api.duckduckgo.com").get.
           addQueryString(
             "q" -> "1 + 1",
             "format" -> "json"
           )
         val f = http.run(r, Gigahorse.asString andThen {_.take(60)})
         Await.result(f, 120.seconds)
       }

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 を有効化したサブプロジェクトを定義するだけだ。

Syndicate content