Skip to content

Latest commit

 

History

History
1298 lines (845 loc) · 87 KB

effectivescala-ja.mo

File metadata and controls

1298 lines (845 loc) · 87 KB
Fork me on GitHub

Effective Scala

Marius Eriksen, Twitter Inc.
[email protected] (@marius)

[translated by Yuta Okamoto (@okapies) and Satoshi Kobayashi (@scova0731)]

Table of Contents

.TOC

他の蚀語

English РусскОй 简䜓䞭文 ## 序章 [Scala][Scala] は Twitter で䜿われおいる䞻芁なアプリケヌションプログラミング蚀語の䞀぀だ。Twitter のむンフラの倧郚分は Scala で曞かれおいるし、我々の業務を支える[倧芏暡ラむブラリ](http://github.com/twitter/)をいく぀か持っおいる。Scala は極めお効果的だが、䞀方で巚倧な蚀語でもある。我々の経隓では、Scala の適甚には十分な泚意が必芁だ。萜ずし穎は䜕か どの機胜を掻甚しお、どれを控えるべきか い぀"玔粋関数型のスタむル"を採甚しお、い぀避けるべきか 蚀い換えるなら、我々が芋出した"Scala の効果的 (effective) な䜿い方"ずは䜕か 本ガむドの目的は、我々の経隓から抜き出された䞀連の*ベストプラクティス*を提䟛するこずだ。Twitter では、䞻に Scala を分散システムを構成する倧容量サヌビス矀の䜜成に䜿っおいるので、我々の助蚀にはバむアスがかかっおいる。しかし、ここにあるアドバむスの倧半は、他の問題領域ぞ自然に移し替えられるはずだ。これは芏則ではない。だから逞脱は正圓化されるべきだ。 Scala が提䟛するたくさんの道具は、簡朔な衚珟を可胜にする。タむピングが枛れば読む量が枛り、倧抵は読む量が枛ればより速く読める。故に、簡朔であるほどより明快になる。しかしたた、簡朔さは正反察の効果をもたらす”なたくら道具”ずもなりえる: 正確さの次には、い぀も読み手のこずを考えよう。 䜕よりも *Scala でプログラムしよう*。君が曞いおいるのは Java ではないし、Haskell でも、Python でもない。Scala のプログラムは、いずれの蚀語で曞かれたものずも異なっおいる。プログラミング蚀語を効果的に䜿うには、君の問題をその蚀語の甚語で衚珟するべきだ。Java のプログラムを無理矢理 Scala で衚珟しおも、ほずんどの堎合オリゞナルより劣ったものになるだろう。 これは Scala の入門ではない。本ガむドは Scala に慣れ芪しんだ読者を前提ずしおいる。これから Scala を孊びたい読者には以䞋のような教材がある: * [Scala School](https://twitter.github.com/scala_school/) * [Learning Scala](https://www.scala-lang.org/node/1305) * [Learning Scala in Small Bites](https://matt.might.net/articles/learning-scala-in-small-bites/) 本ガむドは生きたドキュメントなので、我々の最新の”ベストプラクティス”を反映するために倉曎されるかもしれない。しかし、䞭栞ずなるアむデアが倉わるこずはないだろう: 可読性を垞に優先せよ; 汎甚的なコヌドを曞き、しかし明瞭さを損なわないこず; シンプルな蚀語機胜を掻甚せよ。シンプルさは偉倧な力をもたらし、たた特に型システムにおける難解さを回避できる。ずりわけ、垞にトレヌドオフを意識しよう。掗緎された蚀語は耇雑な実装を芁求し、耇雑さは耇雑さを生む。掚論の耇雑さ、意味論の耇雑さ、機胜間盞互䜜甚の耇雑さ、そしお君の協力者ぞの理解の耇雑さを。したがっお、耇雑さは掗緎がもたらす皎金であり、効甚がコストを䞊回っおいるこずを垞に確認すべきだ。 では、楜しんでほしい。 ## 曞匏 コヌドの*曞匏*の现かい話はそれが実践的である限りは重芁ではない。圓然だが、スタむルに本質的な良し悪しはないし、個人的な奜みはほが人によっお異なる。しかし、同じ敎圢ルヌルを*䞀貫しお*適甚すれば、ほずんどの堎合で可読性が高たる。特定のスタむルに銎染んだ読み手は、さらに別のロヌカルな慣習を把握したり、蚀語の文法の隅を解読したりする必芁がない。 これは、重耇床の高い文法を持぀ Scala においおは特に重芁だ。メ゜ッド呌び出しは分かりやすい䟋だ: メ゜ッドは "`.`" を付けおも、ホワむトスペヌスで空けおも呌び出せる。同様に、れロたたは䞀぀匕数を取るメ゜ッドでは、䞞カッコを付けおも良いし付けなくおも良い、ずいった様に。さらに、メ゜ッド呌び出しの様々なスタむルは、文法䞊の様々な曖昧さをさらけ出す 泚意深く遞ばれた敎圢ルヌルを䞀貫しお適甚するこずで、人間ず機械の䞡方にずっおの倚くの曖昧さを解決できるのは間違いない。 我々は、[Scala style guide](http://docs.scala-lang.org/style/) を順守するず共に以䞋のルヌルを远加した。 ### ホワむトスペヌス むンデントは、スペヌス 2 ぀ずする。100 カラムを超える行は避ける。メ゜ッド、クラス、オブゞェクトの定矩同士の間に䞀行空ける。 ### 呜名
小さいスコヌプでは短い名前を䜿う
ルヌプ内では i, j, k が期埅される。
より倧きいスコヌプでは、より長い名前を䜿う
倖郚APIは、より長く、説明的で意味付けされた名前を持぀べきだ。䟋えば、Future.all ではなく Future.collect ずした方がよい。
䞀般的な略語を䜿い、難解な略語を避ける
ok や err や defn は誰もが知っおいる。しかし sfri はそれほど䞀般的ではない。
異なる甚途に名前を再利甚しない
val を䜿おう。
予玄名を ` でオヌバヌロヌドするのは避ける
`type` ではなく typ ずする。
副䜜甚を起こす操䜜は胜動態で呜名する
user.setActive() ではなく user.activate() ずする。
倀を返すメ゜ッドは説明的に呜名する
src.defined ではなく src.isDefined ずする。
ゲッタヌ (getter) の名前の先頭に get を付けない
以前のルヌルず同様にこれは冗長だ。site.getCount ではなく site.count ずする。
パッケヌゞ名やオブゞェクト名で既にカプセル化されおいる名前を繰り返さない
object User {
  def getUser(id: Int): Option[User]
}
ではなく
object User {
  def get(id: Int): Option[User]
}
ずする。User.getUser は冗長だし、User.get よりも倚くの情報を䞎えない。
### むンポヌト
import 行はアルファベット順に䞊べ替える
こうするず芖芚的に調べやすいし自動化もしやすい。
パッケヌゞから耇数の名前をむンポヌトする際は䞭カッコを䜿う
import com.twitter.concurrent.{Broker, Offer}
6 個以䞊の名前をむンポヌトする際はワむルドカヌドを䜿う
䟋: import com.twitter.concurrent._
ワむルドカヌドを濫甚しないこず。䞀郚のパッケヌゞは倧量の名前を゚クスポヌトする。
コレクションを䜿う際は、scala.collection.immutable ず scala.collection.mutable の䞀方あるいは䞡方をむンポヌトしお名前を修食する
可倉 (mutable) コレクションず䞍倉 (immutable) コレクションでは名前が重耇しおいる。名前を修食しお、どちらのコレクションを䜿っおいるか読み手に察しお明らかにしよう。 (䟋: "immutable.Map")
他のパッケヌゞからの盞察指定でむンポヌトしない
import com.twitter
import concurrent
ずは曞かずに曖昧さの無い曞き方をしよう。
import com.twitter.concurrent
import 文はファむルの先頭に眮く
党おの import を読み手が䞀箇所で参照できるようにしよう。
### 䞭カッコ 䞭カッコは耇合匏を䜜るのに䜿われる"モゞュヌル蚀語"では他の甚途にも䜿われる。このずき、耇合匏の倀はリスト䞭の最埌の匏だ。単玔な匏に䞭カッコを䜿うのはやめよう。 def square(x: Int) = x*x .LP ず曞く代わりに、メ゜ッドの本䜓を構文的に芋分けられるように def square(x: Int) = { x * x } .LP ず曞きたくなるかもしれない。しかし、最初の方がゎチャゎチャしおいなくお読みやすい。明確化が目的でないなら仰々しい構文を䜿うのはやめよう。 ### パタヌンマッチ 関数定矩の䞭で、パタヌンマッチを盎接䜿える堎合はい぀でもそうしよう。 list map { item => item match { case Some(x) => x case None => default } } .LP ずいう間接的な曞き方では意図がはっきりしない。代わりに match を折り畳んで list map { case Some(x) => x case None => default } .LP ず曞くず、リストの芁玠を写像 (map over) しおいるこずが分かりやすい。 ### コメント [ScalaDoc](https://wiki.scala-lang.org/display/SW/Scaladoc) を䜿っお API ドキュメントを提䟛しよう。以䞋のスタむルで曞こう: /** * ServiceBuilder builds services * ... */ .LP しかし、暙準の ScalaDoc スタむルは䜿わない方がいい: /** ServiceBuilder builds services * ... */ アスキヌアヌトや芖芚的な装食に頌っおはいけない。たた、API ではない䞍必芁なコメントをドキュメント化すべきでない。もし、コヌドの挙動を説明するためにコメントを远加しおいるのに気づいたら、たずは、それが䜕をするコヌドなのか明癜になるよう再構築できないか考えおみよう。”芋るからに、それは動䜜する (it works, obviously)”よりも”明らかにそれは動䜜する (obviously it works)”方がいいホヌアには申し蚳ないけど^[蚳泚: [アントニヌ・ホヌア](http://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%88%E3%83%8B%E3%83%BC%E3%83%BB%E3%83%9B%E3%83%BC%E3%82%A2)は、自身のチュヌリング賞受賞講挔で*「極めお耇雑に蚭蚈しお”明らかな”欠陥を無くすより、非垞に簡玠に蚭蚈しお”明らかに”欠陥が無いようにする方が遥かに難しい」*ずいう趣旚の発蚀をしおいる。䞀方、著者は「コヌドから実装の意図を䞀目瞭然に読み取れるようにせよ」ずいう立堎であり、぀たりホヌアの䞻匵ずは真逆になる。]。 ## 型ずゞェネリクス 型システム (type system) の䞻な目的は、プログラミングの誀りを怜出するこずだ。型システムは、制限された静的怜査を効果的に提䟛する。これを䜿うず、コヌドに぀いおある皮の䞍倉条件 (invariant) を蚘述しお、それをコンパむラで怜蚌できる。型システムがもたらす恩恵はもちろん他にもあるが、゚ラヌチェックこそ、その存圚理由レヌゟンデヌトルだ。 我々が型システムを䜿う堎合はこの目的を螏たえるべきだが、䞀方で、読み手にも気を配り続ける必芁がある。型を慎重に䜿ったコヌドは明瞭さが高たるが、過剰に巧劙に䜿ったコヌドは読みにくいだけだ。 Scala の匷力な型システムは、孊術的な探求ず挔習においおよく題材ずされる (䟋: [Type level programming in Scala](http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/))。これらのテクニックは孊術的に興味深いトピックだが、プロダクションコヌドでの応甚においお有甚であるこずは皀だ。避けるべきだろう。 ### 戻り型アノテヌション Scala では戻り型アノテヌション (return type annotation) を省略できるが、䞀方でアノテヌションは優れたドキュメンテヌションを提䟛する。特に、public メ゜ッドにずっおは重芁だ。戻り型が明癜で露出しおいないメ゜ッドの堎合は省略しよう。 これは、ミックスむンを䜿ったオブゞェクトのむンスタンス化においお、Scala コンパむラがシングルトン型を生成する際に特に重芁だ。䟋えば、`make` 関数が: trait Service def make() = new Service { def getId = 123 } .LP Service ずいう戻り型を持たない堎合、コンパむラは现別型 (refinement type) の Object with Service{def getId: Int} を生成する。代わりに、明瀺的なアノテヌションを䜿うず: def make(): Service = new Service{} `make` の公開する型を倉曎するこずなく、トレむトをさらに奜きなだけミックスできる。぀たり、埌方互換性の管理が容易になる。 ### 倉䜍 倉䜍 (variance) は、ゞェネリクスが掟生型 (subtyping) ず結び぀く際に珟れる。倉䜍は、コンテナ型 (*container* type) の掟生型ず、芁玠型 (*contained* type) の掟生型がどう関連するかを定矩する。Scala では倉䜍アノテヌションを宣蚀できるので、コレクションに代衚される共通ラむブラリの䜜者は、倚数のアノテヌションを扱う必芁がある。倉䜍アノテヌションは共有コヌドの䜿い勝手にずっお重芁だが、誀甚するず危険なものになりうる。 非倉 (invariant) は高床だが、Scala の型システムにずっお必須の特城であり、掟生型の適甚を助けるために広くそしお正しく䜿われるべきだ。 *䞍倉コレクションは共倉 (covariant) であるべきだ*。芁玠型を受け取るメ゜ッドは、コレクションを適切に"栌䞋げ"すべきだ: trait Collection[+T] { def add[U >: T](other: U): Collection[U] } *可倉コレクションは非倉であるべきだ*。共倉は、通垞は可倉コレクションにおいおは無効だ。この trait HashSet[+T] { def add[U >: T](item: U) } .LP ず、以䞋の型階局に぀いお考えおみよう: trait Mammal trait Dog extends Mammal trait Cat extends Mammal .LP もしここに犬 (Dog) のハッシュセットがあるなら、 val dogs: HashSet[Dog] .LP それを哺乳類 (Mammal) の集合ずしお扱ったり、猫 (Cat) を远加したりできる。 val mammals: HashSet[Mammal] = dogs mammals.add(new Cat{}) .LP これはもはや、犬の HashSet ではない ### 型゚むリアス 型゚むリアス (type alias) を䜿うず、䟿利な名前を提䟛したり、意味を明瞭にしたりできる。しかし、䞀目瞭然な型を゚むリアスすべきではない。 () => Int .LP は、短くお䞀般的な型を䜿っおいるので、 type IntMaker = () => Int IntMaker .LP よりも意味が明瞭だ。しかし、 class ConcurrentPool[K, V] { type Queue = ConcurrentLinkedQueue[V] type Map = ConcurrentHashMap[K, Queue] ... } .LP は、意思疎通が目的で簡朔さを高めたい堎合に有甚だ。 ゚むリアスが䜿える堎合は、サブクラスにしおはいけない。 trait SocketFactory extends (SocketAddress => Socket) .LP SocketFactory は Socket を生成する関数だが、型゚むリアス type SocketFactory = SocketAddress => Socket .LP を䜿う方がいい。これで、SocketFactory 型の倀ずなる関数リテラルが提䟛されるので、関数合成が䜿える: val addrToInet: SocketAddress => Long val inetToSocket: Long => Socket val factory: SocketFactory = addrToInet andThen inetToSocket パッケヌゞオブゞェクトを䜿うず、型゚むリアスをトップレベル名ずしお束瞛できる: package com.twitter package object net { type SocketFactory = (SocketAddress) => Socket } 型゚むリアスは新しい型ではないこずに泚意しよう。型゚むリアスは、゚むリアスされた名前をその型ぞず構文的に眮換するこずず同等だ。 ### 暗黙 暗黙 (implicit) は匷力な型システムの機胜だが、慎重に䜿うべきだ。それらの解決ルヌルは耇雑で、シンプルな字句怜査においおさえ、実際に䜕が起きおいるか把握するのを困難にする。暗黙を間違いなく䜿っおもいいのは以䞋の状況だ: * Scala スタむルのコレクションを拡匵したり、远加したりするずき * オブゞェクトを適合 (adapt) したり、拡匵 (extend) したりするずき"pimp my library" パタヌン * [制玄゚ビデンス](http://www.ne.jp/asahi/hishidama/home/tech/scala/generics.html#h_generalized_type_constraints)を提䟛しお*型安党を匷化*するために䜿う * 型゚ビデンス型クラスを提䟛するため * `Manifest`Scala 2.10 以降は `TypeTag`のため 暗黙を䜿おうずする時は、暗黙を䜿わずに同じこずを達成する方法がないか垞に確認しよう。 䌌通ったデヌタ型同士を、自動的に倉換するために暗黙を䜿うのはやめよう䟋えば、リストをストリヌムに倉換する等。明瀺的に倉換するべきだ。それらの型はそれぞれ異なった動䜜をするので、読み手は暗黙の型倉換が働いおいないか泚意しなくおはならなくなる。 ## コレクション Scala のコレクションラむブラリは非垞に総称的 (generic) で、機胜が豊富で、匷力で、組み立おやすい。コレクションは高氎準であり、倚数の操䜜を公開しおいる。倚くのコレクション操䜜ず倉換を簡朔か぀読みやすく衚珟できるが、そうした機胜を䞍泚意に適甚するず、しばしば正反察の結果を招く。党おの Scala プログラマは [collections design document](http://docs.scala-lang.org/ja/overviews/collections/introduction.html) を読むべきだ。このドキュメントは、Scala のコレクションラむブラリに察する優れた掞察ずモチベヌションをもたらしおくれる。 垞に、芁求を最もシンプルに満たすコレクションを䜿おう。 ### 階局 Scala のコレクションラむブラリは巚倧だ。`Traversable[T]` を基底ずする入り組んだ継承階局だけでなく、ほずんどのコレクションに `immutable` 版ず `mutable` 版がある。どんなに耇雑でも、以䞋の図は `immutable` ず `mutable` の双方の階局にずっお重芁な区別を含んでいる。

.cmd pic2graph -format png >coll.png <Iterable[T] はむテレヌト (iterate) できるコレクションで、iterator (ず foreach) メ゜ッドを提䟛する。Seq[T] は順序付けされたコレクション、Set[T] は数孊的集合芁玠が䞀意な順序付けのないコレクション、そしお Map[T] は順序付けのない連想配列だ。 ### 䜿う *䞍倉 (immutable) コレクションを䜿おう。*䞍倉コレクションはほずんどの状況に適甚できる。たた、䞍倉コレクションは参照透過なのでデフォルトでスレッドセヌフずなり、プログラムの理解が容易になる。 *明瀺的に `mutable` 名前空間を䜿おう。*`scala.collections.mutable._` を import しお `Set` を参照するのではなく、 import scala.collections.mutable val set = mutable.Set() .LP ずするこずで、可倉版の `Set` が䜿われおいるこずがはっきりする。 *コレクション型のデフォルトコンストラクタを䜿おう。*䟋えば、順序付きのか぀連結リストである必芁がないシヌケンスが必芁な堎合は、い぀でも `Seq()` コンストラクタを䜿おう: val seq = Seq(1, 2, 3) val set = Set(1, 2, 3) val map = Map(1 -> "one", 2 -> "two", 3 -> "three") .LP このスタむルでは、コレクションの動䜜ずその実装が切り分けられるので、コレクションラむブラリに察しお最も適切な実装型を䜿わせるこずができる。君が必芁なのは Map であっお、必ずしも赀黒朚ではない。さらに、これらのデフォルトコンストラクタは、しばしば特殊化された衚珟を甚いる。䟋えば、Map() は 3 ぀のキヌを持぀マップに察しお、フィヌルドを 3 ぀持぀オブゞェクトMap3 クラスを䜿う。 以䞊の圓然の垰結ずしお、メ゜ッドやコンストラクタでは、*最も総称的なコレクション型を適切に受け取ろう*。芁するに、通垞は䞊蚘の `Iterable`, `Seq`, `Set`、あるいは `Map` のうちのどれか䞀぀だ。シヌケンスが必芁なメ゜ッドには `List[T]` ではなく `Seq[T]` を䜿おう。 .LP 泚意: `scala.package` が定矩するデフォルトの `Traversable`、`Iterable` ず `Seq` は `scala.collection` バヌゞョンだ。これに察しお、`Predef.scala` が定矩する `Map` ず `Set` は `scala.collection.immutable` バヌゞョンだ。これが意味するのは、䟋えば、デフォルトの `Seq` 型は䞍倉ず可倉の*äž¡æ–¹*になれるずいうこずだ。したがっお、君のメ゜ッドの匕数が䞍倉コレクションに䟝存するなら、 `Traversable`、`Iterable` や `Seq` を䜿う堎合、明確に䞍倉バヌゞョンを require/import する必芁がある。さもなければ、メ゜ッドに可倉バヌゞョンが枡されるかもしれない。 ### スタむル 関数型プログラミングでは、パむプラむン化した䞍倉コレクションの倉換によっお、コレクションを望みの結果ぞず成圢するこずが掚奚されおいる。この手法により、倚くの問題をずおも簡朔に解決できるが、これは読み手を困惑させる可胜性がある。パむプラむン化した倉換は時に䜜者の意図の理解を困難にするので、その堎合、暗黙的に瀺される䞭間結果を党お远跡し続けるしかない。䟋えば、様々なプログラミング蚀語に察する投祚である (language, num votes) のシヌケンスを集蚈しお、祚数が最も倚い蚀語から順番に衚瀺するコヌドは以䞋のように曞ける: val votes = Seq(("scala", 1), ("java", 4), ("scala", 10), ("scala", 1), ("python", 10)) val orderedVotes = votes .groupBy(_._1) .map { case (which, counts) => (which, counts.foldLeft(0)(_ + _._2)) }.toSeq .sortBy(_._2) .reverse .LP このコヌドは簡朔でか぀正しい。しかし、ほずんどの読み手は䜜者の元々の意図を把握するのに苊劎するだろう。䞭間結果ずパラメヌタに名前を付ける戊略は、倚くの堎合で䜜者の意図を明確にするのに圹立぀: val votesByLang = votes groupBy { case (lang, _) => lang } val sumByLang = votesByLang map { case (lang, counts) => val countsOnly = counts map { case (_, count) => count } (lang, countsOnly.sum) } val orderedVotes = sumByLang.toSeq .sortBy { case (_, count) => count } .reverse .LP このコヌドでは、斜される倉換を䞭間倀の名前ずしお、操䜜されるデヌタ構造をパラメヌタ名ずしお衚しおいる。これにより、以前ず同じくらい簡朔であるだけでなく、よりいっそう明瞭な衚珟ずなっおいる。もし名前空間の汚染が心配なら、匏を {} でグルヌプ化するず良い: val orderedVotes = { val votesByLang = ... ... } ### 性胜 高氎準コレクションラむブラリは高氎準な構築物が䞀般的にそうであるように性胜の掚枬が難しい。コンピュヌタに盎接指瀺する”呜什型スタむル”から遠ざかるほど、あるコヌド片が性胜に䞎える圱響を厳密に予枬するのは困難になる。䞀方で、コヌドの正確さを刀断するのは抂しお容易だし、読みやすさも高たる。Scala の堎合、Java ランタむムが事態をさらに耇雑にしおいる。Scala はナヌザに察しおボクシング (boxing) およびアンボクシング (unboxing) 操䜜を隠蔜するので、性胜やメモリ䜿甚量の面で重倧なペナルティを被るこずがある。 䜎レベルにおける现郚に泚目する前に、君の甚途に察しおコレクションの䜿い方が適切かどうか確認しよう。たた、デヌタ構造に予期しない挞近的な耇雑さがないか確かめよう。Scala のさたざたなコレクションの耇雑さに぀いおは[こちら](http://docs.scala-lang.org/ja/overviews/collections/performance-characteristics.html)で述べられおいる。 性胜最適化の第䞀法則は、君のアプリケヌションが*なぜ*遅いのかを理解するこずだ。最適化を始める前に、君のアプリケヌションをプロファむル^[[Yourkit](http://yourkit.com)は良いプロファむラだ。]しおデヌタを取ろう。最初に泚目するのは、回数の倚いルヌプや巚倧なデヌタ構造だ。最適化ぞの過床な取り組みは、たいおい無駄な努力に終わる。クヌヌスの”時期尚早な最適化は諞悪の根源”ずいう栌蚀を思い出そう。 性胜やメモリ効率が芁求される堎面では、倚くの堎合で䜎レベルコレクションを䜿うのが劥圓だ。巚倧なシヌケンスには、リストより配列を䜿おう䞍倉の `Vector` コレクションは、配列ぞの参照透過なむンタフェヌスを提䟛する。たた、性胜が重芁な堎合は、シヌケンスを盎接生成せずにバッファを䜿おう。 ### Java コレクション Java コレクションず Scala コレクションず盞互運甚するには、`scala.collection.JavaConverters` を䜿おう。`JavaConverters` は、暗黙倉換を行う `asJava` メ゜ッドず `asScala` メ゜ッドを远加する。読み手のために、これらの倉換は明瀺的に行うようにしよう: import scala.collection.JavaConverters._ val list: java.util.List[Int] = Seq(1,2,3,4).asJava val buffer: scala.collection.mutable.Buffer[Int] = list.asScala ## 䞊行性 珟代のサヌビスは高い䞊行性 (concurrency) を備え、サヌバが䜕䞇䜕十䞇もの同時操䜜をコヌディネヌトするのが䞀般的になっおいる。そしお、堅固なシステム゜フトりェアを蚘述する䞊で、暗黙的な耇雑性ぞの察凊は䞭心的なテヌマだ。 *スレッド (thread)* は、䞊行性を衚珟する手段の䞀぀だ。スレッドを䜿うこずで、オペレヌティングシステムによっおスケゞュヌルされる、ヒヌプを共有する独立した実行コンテクストを利甚できる。しかし、Java においおスレッド生成はコストが高いので、兞型的にはスレッドプヌルを䜿うこずで、スレッドをリ゜ヌスずしお管理する必芁がある。これは、プログラマにずっおさらなる耇雑さず高い結合床を生み出す。぀たり、アプリケヌションロゞックず、それが䜿甚する朜圚的なリ゜ヌスを分離するのが難しくなる。 この耇雑さは、出力 (fan-out) の倧きいサヌビスを䜜成する際に、ずりわけ明らかになる。それぞれの受信リク゚ストからは、システムのさらに別の階局に察する倚数のリク゚ストが生じる。それらのシステムにおいお、スレッドプヌルは各階局でのリク゚ストの割合によっおバランスを保぀ように管理される必芁がある。あるスレッドプヌルで管理に倱敗するず、その圱響は他のスレッドプヌルにも広がっおしたう。 たた、堅固なシステムはタむムアりトずキャンセルに぀いおも怜蚎する必芁がある。どちらに察凊するにも、さらなる”制埡スレッド”を導入する必芁があるので、問題がさらに耇雑になる。ちなみに、もしスレッドのコストが安いなら問題は䜎枛できる。スレッドプヌルが必芁なくなり、タむムアりトしたスレッドを攟棄するこずができ、远加のリ゜ヌス管理も必芁ないからだ。 このように、リ゜ヌス管理はモゞュヌル性を危うくするのだ。 ### Future Future で䞊行性を管理しよう。Future は、䞊行操䜜ずリ゜ヌス管理を疎結合にする。䟋えば、[Finagle][Finagle] はわずかな数のスレッド䞊で䞊行操䜜を効率的に倚重化する。Scala には軜量なクロヌゞャリテラル構文があるので、Future のために新たな構文を芚える必芁がなく、ほずんどのプログラマにずっお自然に扱える。 Future は、プログラマが䞊行蚈算を宣蚀的なスタむルで衚珟できるようにする。Future は合成可胜で、たた蚈算の倱敗を䞀定の原則に基づいお凊理できる。こうした性質は Future は関数型プログラミング蚀語にずおも適しおおり、掚奚されるスタむルだず確信しおいる。 *生成した Future を倉換しよう。*Future の倉換は、倱敗の䌝播やキャンセルの通知が行われるこずを保蚌し、たたプログラマが Java メモリモデルの圱響を怜蚎する必芁がなくなる。泚意深いプログラマでさえ、RPC を逐次的に 10 回発行しお結果を衚瀺するプログラマを以䞋のように曞いおしたうかもしれない: val p = new Promise[List[Result]] var results: List[Result] = Nil def collect() { doRpc() onSuccess { result => results = result :: results if (results.length < 10) collect() else p.setValue(results) } onFailure { t => p.setException(t) } } collect() p onSuccess { results => printf("Got results %s\n", results.mkString(", ")) } プログラマは、RPC の倱敗が確実に䌝播するように、コヌドに制埡フロヌをいく぀も挿入する必芁がある。さらに悪いこずに、このコヌドは間違っおいる `results` を `volatile` ずしお宣蚀しおいないので、繰り返しごずに `results` が䞀぀前の倀を保持しおいるこずを保蚌できない。Javaのメモリモデルは、油断ならない獣だ。しかし幞いなこずに、宣蚀的スタむルを䜿えばこれらの萜ずし穎を党お避けるこずができる: def collect(results: List[Result] = Nil): Future[List[Result]] = doRpc() flatMap { result => if (results.length < 9) collect(result :: results) else Future.value(result :: results) } collect() onSuccess { results => printf("Got results %s\n", results.mkString(", ")) } ここでは `flatMap` を䜿っお操䜜を順序付けし、凊理が進むに぀れおリストの先頭に結果を远加しおいる。これは、関数型プログラミングの䞀般的なむディオムを Future に眮き換えたものだ。これは正しく動䜜するだけでなく、必芁な”おたじない”が少なくなるし、゚ラヌの枩床が枛り、そしお読みやすい。 *Future のコンビネヌタ (combinator) を䜿おう。*`Future.select`, `Future.join`, `Future.collect` は、耇数の Future を組み合わせお操䜜する際の䞀般的なパタヌンを䜓系化しおいる。 ### コレクション 䞊行コレクションの話題は、私芋ず、機埮ず、ドグマず、FUD に満ちおいる。それらは、倧抵の実践的な状況においおは取るに足らない問題だ: 目的を果たすためには、い぀でも最も単玔で、最も退屈で、最も暙準的なコレクションから始めよう。同期化コレクションでは䞊手くいかないこずが*分かる*前に、䞊行コレクションを手に取っおはいけない。JVM は、同期を䜎コストで実珟する掗緎された機構を持っおいる。その効率に君は驚くだろう。 䞍倉 (immutable) コレクションで目的を果たせるなら、それを䜿おう。䞍倉コレクションは参照透過なので、䞊行コンテキストでの掚論が簡単になる。䞍倉コレクションの倉曎は、䞻に`var` セルや `AtomicReference` が指す珟圚の倀ぞの参照を曎新するこずで行う。䞍倉コレクションの倉曎は泚意が必芁だ。他のスレッドぞ䞍倉コレクションを公開するには、`AtomicReference` は再詊行が必芁だし、`var` 倉数は `volatile` ずしお宣蚀しなければならない。 可倉 (mutable) な䞊行コレクションは耇雑な動䜜をするだけでなく、Java メモリモデルの埮劙な郚分を利甚するので、特に曎新を公開する方法などの暗黙的な挙動を理解しおおこう。同期化コレクションの方が合成は簡単だ。䞊行コレクションでは `getOrElseUpdate` のような操䜜を正しく実装できないし、特に䞊行コレクションの合成ぱラヌの枩床になる。 ## 制埡構造 関数型スタむルのプログラムは、埓来の制埡構造が少なくなり、たた、宣蚀型スタむルで曞かれおいるず読みやすいこずが倚い。こうしたスタむルでは、兞型的にはロゞックをいく぀かの小さなメ゜ッドや関数に分解し、それらを互いに `match` 匏で貌り合わせる。たた、関数型プログラムは、より匏指向ずなる傟向がある: ぀たり、条件匏のそれぞれの分岐は同じ型の倀を蚈算し、`for (..) yield` は内包 (comprehension) を蚈算する。たた、再垰の利甚が䞀般的だ。 ### 再垰 *再垰衚珟を䜿うず、問題をしばしば簡朔に蚘述できる。*そしおコンパむラは、末尟呌び出しの最適化が適甚できるコヌドを正芏のルヌプに眮き換える末尟最適化が適甚されるかは `@tailrec` アノテヌションで確認できる。 ヒヌプの fix-down アルゎリズムの、極めお暙準的な呜什型バヌゞョンを怜蚎しよう: def fixDown(heap: Array[T], m: Int, n: Int): Unit = { var k: Int = m while (n >= 2*k) { var j = 2*k if (j < n && heap(j) < heap(j + 1)) j += 1 if (heap(k) >= heap(j)) return else { swap(heap, k, j) k = j } } } このコヌドでは、while ルヌプに入るたびに䞀぀前の反埩で倉曎された状態を参照する。各倉数の倀は、どの分岐を取るかに䟝存する。そしお、正しい䜍眮が芋぀かるずルヌプの䞭盀で `return` する鋭い読者は、ダむクストラの ["Go To Statement Considered Harmful"](http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html) ^[蚳泚: [゚ドガヌ・ダむクストラ](http://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%89%E3%82%AC%E3%83%BC%E3%83%BB%E3%83%80%E3%82%A4%E3%82%AF%E3%82%B9%E3%83%88%E3%83%A9)は、構造化プログラミングの提唱者。圌が執筆した゚ッセむ "Go To Statement Considered Harmful" は”GOTO有害論”の端緒ずしお有名。] に同様の議論があるこずに気づくず思う。 末尟再垰による実装を怜蚎しよう^[[Finagle's heap balancer](https://github.com/twitter/finagle/blob/master/finagle-core/src/main/scala/com/twitter/finagle/loadbalancer/Heap.scala#L41)より]: @tailrec final def fixDown(heap: Array[T], i: Int, j: Int) { if (j < i*2) return val m = if (j == i*2 || heap(2*i) < heap(2*i+1)) 2*i else 2*i + 1 if (heap(m) < heap(i)) { swap(heap, i, m) fixDown(heap, m, j) } } .LP ここでは、すべおの反埩は明確に定矩された癜玙の状態から開始する。たた、参照セルが存圚しないので䞍倉匏 (invariant) を数倚く芋出せる。このメ゜ッドはより掚論しやすく、より読みやすい。さらに、性胜面のペナルティもない。メ゜ッドは末尟再垰なので、コンパむラがこれを暙準的な呜什型のルヌプぞず倉換するからだ。 ### Return 前節では再垰を䜿うメリットを玹介したが、ずはいえ呜什型の構造が無䟡倀だずいうわけではない。倚くの堎合、蚈算を早期に終了する方が、終点の可胜性がある党おの䜍眮に条件分岐を持぀よりも適切だ。実際に、䞊蚘の `fixDown` がヒヌプの終端に達するず `return` によっお早期に終了する。 `return` を䜿うず、分岐を枛らしお䞍倉匏 (invariant) を定めるこずができる。これにより、入れ子が枛っおコヌドを远いやすくなるだけでなく、埌続のコヌドの正圓性を掚論しやすくなる配列の範囲倖をアクセスしないこずを確認する堎合ずか。これは、"ガヌド"節で特に有甚だ: def compare(a: AnyRef, b: AnyRef): Int = { if (a eq b) return 0 val d = System.identityHashCode(a) compare System.identityHashCode(b) if (d != 0) return d // slow path.. } `return` を䜿っお、コヌドを明快にしお読みやすさを高めよう。ただし、呜什型蚀語でのような䜿い方をしおはいけない。぀たり、蚈算結果を返すために `return` を䜿うのは避けよう。 def suffix(i: Int) = { if (i == 1) return "st" else if (i == 2) return "nd" else if (i == 3) return "rd" else return "th" } .LP ず曞く代わりに䞋蚘のように曞こう: def suffix(i: Int) = if (i == 1) "st" else if (i == 2) "nd" else if (i == 3) "rd" else "th" .LP しかし、match 匏を䜿うのがより優れた方法だ: def suffix(i: Int) = i match { case 1 => "st" case 2 => "nd" case 3 => "rd" case _ => "th" } なお、クロヌゞャの内郚で `return` を䜿うず目に芋えないコストが発生する堎合があるので泚意しよう。 seq foreach { elem => if (elem.isLast) return // process... } .LP この `return` は、バむトコヌドでは䟋倖を `throw` ず `catch` するコヌドずしお実装されるので、実行頻床の高いコヌド内で䜿うず性胜に圱響を䞎える。 ### `for`ルヌプず内包 `for` を䜿うず、ルヌプず集玄を簡朔か぀自然に衚珟できる。`for` は、倚数のシヌケンスを平坊化 (flatten) する堎合に特に有甚だ。`for` の構文は、内郚的にはクロヌゞャを割り圓おおディスパッチしおいるこずを芆い隠しおいる。これにより予期しないコストが発生したり、予想倖の挙動を瀺したりする。䟋えば、 for (item <- container) { if (item != 2) return } .LP このコヌドは `container` が遅延評䟡されるずランタむム゚ラヌが発生し、これにより return が非局所的 (nonlocal) に評䟡されおしたう これらの理由から、コヌドを明瞭にするためである堎合を陀いお、`for` の代わりに `foreach`, `flatMap`, `map`, `filter` を盎接呌び出すのが良いこずが倚い。 .LP 蚳者による補足: Scala の for 匏は `foreach`, `flatMap`, `map`, `withFilter` を呌び出す糖衣構文で、ルヌプ内の匏は、コンパむル時にそれらのメ゜ッドに枡される匿名関数に倉換される。䟋えば、䞊蚘の for 匏は: container foreach { item => if (item != 2) return } .LP ずいうコヌドずしお実行される。本ガむドでは、最初からこのように蚘述するこずを掚奚しおいる。 ずころで、ネストした匿名関数での `return` 匏は、ランタむム゚ラヌである `NonLocalReturnException` の `throw` ず `catch` に倉換される。この堎合、for 匏の䞭の `container` が遅延評䟡されるず `return` 匏の挙動が意図しないものになる堎合がある。詳现に興味がある読者は[こちらの議論](https://github.com/scalajp/effectivescala/commit/8b448ef819e6d87d21fa78310b84fc72593b0226#commitcomment-996948)も参照しおほしい。 ### `require` ず `assert` `require` ず `assert` は、どちらも実行可胜なドキュメントずしお機胜する。これらは、型システムが芁求される䞍倉条件 (invariant) を衚珟できない状況で有甚だ。`assert` は、コヌドが仮定する内郚あるいは倖郚の*䞍倉条件*を衚珟するために䜿われる。䟋えば、 val stream = getClass.getResourceAsStream("someclassdata") assert(stream != null) 䞀方で、`require` は API の契玄を衚珟するために䜿われる: def fib(n: Int) = { require(n > 0) ... } ## 関数型プログラミング *倀指向*プログラミングは、特に関数型プログラミングず䞀緒に甚いるこずで数々の利益をもたらす。このスタむルはステヌトフルな倉曎よりも倀の倉換を重芖し、参照透過 (referentially transparent) なコヌドを生み出し、より匷力な䞍倉匏 (invariant) を提䟛するので、掚論が容易になる。ケヌスクラス、パタヌンマッチ、構造化代入 (destructuring binding) 、型掚論、クロヌゞャやメ゜ッドの軜量な生成構文がこの仕事の道具になる。 ### 代数的デヌタ型ずしおのケヌスクラス ケヌスクラス (case class) は、代数的デヌタ型 (algebraic data type) を゚ンコヌドする: ケヌスクラスは数倚くのデヌタ構造をモデリングするのに圹に立ち、匷力な䞍倉匏を簡朔なコヌドずしお提䟛する。ケヌスクラスは、パタヌンマッチず共に利甚するず特に有甚だ。パタヌンマッチの解析噚は、さらに匷力な静的保蚌を提䟛する包括的解析 (exhaustivity analysis) を実装しおいる。 ケヌスクラスで代数的デヌタ型を゚ンコヌドする際は、以䞋のパタヌンを䜿おう: sealed trait Tree[T] case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T] case class Leaf[T](value: T) extends Tree[T] .LP Tree[T] 型には Node ず Leaf の 2 ぀のコンストラクタがある。型を sealed ずしお宣蚀するず゜ヌスファむルの倖でコンストラクタを远加できなくなるので、コンパむラに包括的解析を行わせるこずができる。 パタヌンマッチず共に䜿うず、䞊蚘のモデリングは簡朔でありか぀”明らかに正しい”コヌドになる: def findMin[T <: Ordered[T]](tree: Tree[T]) = tree match { case Node(left, right) => Seq(findMin(left), findMin(right)).min case Leaf(value) => value } 朚構造のような再垰的構造は、代数的デヌタ型の叀兞的な応甚を占めるが、代数的デヌタ型が有甚な領域はそれよりずっず倧きい。特に、状態機械によく珟れる盎和 (disjoint union) は、代数的デヌタ型で容易にモデル化できる。 ### Option `Option` 型は、空の状態 (`None`) ず満たされた状態 (`Some(value)`) のいずれかであるコンテナだ。Option は `null` の安党な代替手段を提䟛するので、い぀でも可胜な限り利甚するべきだ。`Option` はたかだか芁玠が䞀぀のコレクションなので、コレクション操䜜を利甚できる。䜿うしかない! 以䞋のように曞くのは安党ではない: var username: String = null ... username = "foobar" .LP 以䞋のように曞こう: var username: Option[String] = None ... username = Some("foobar") .LP Option 型は、username が空であるかのチェックを静的に匷制しおくれるのでより安党だ。 `Option` 倀の条件実行は `foreach` を䜿うべきだ。以䞋のように曞く代わりに: if (opt.isDefined) operate(opt.get) .LP 以䞋のように曞こう: opt foreach { value => operate(value) } このスタむルは奇劙に思えるかもしれないが、より優れた安党性ず簡朔さを提䟛する䟋倖を匕き起こしうる `get` は呌んでいない。空の堎合ず倀を持぀堎合の䞡方の分岐がありうるなら、パタヌンマッチを䜿おう: opt match { case Some(value) => operate(value) case None => defaultAction() } .LP しかし、もし倀がない堎合にデフォルト倀で良いなら getOrElse がある。 operate(opt getOrElse defaultValue) `Option` の濫甚はよくない: もし、[*Null Object*](http://en.wikipedia.org/wiki/Null_Object_pattern) のような目的にあったデフォルト倀があるなら、代わりにそれを䜿おう。 たた `Option` は、null になり埗る倀をラップできる、扱いやすいコンストラクタず共に䜿おう: Option(getClass.getResourceAsStream("foo")) .LP は Option[InputStream] だが、getResourceAsStream が null を返す堎合に None を返す。 ### パタヌンマッチ パタヌンマッチ (`x match { ...`) は、うたく曞かれた Scala コヌドのあらゆる堎所で䜿われる。パタヌンマッチは、条件実行ず分解、そしお䞀぀の構成物ぞのキャストの 3 ぀を融合する。うたく䜿うこずで明快さず安党性の双方を高められる。 型ごずに凊理を切り替える実装にパタヌンマッチを䜿う: obj match { case str: String => ... case addr: SocketAddress => ... たた、パタヌンマッチは分解ず組み合わせるこずで最も良い働きを瀺す。ケヌスクラスにマッチする際は、以䞋のように曞かずに: animal match { case dog: Dog => "dog (%s)".format(dog.breed) case _ => animal.species } .LP 以䞋のように曞く: animal match { case Dog(breed) => "dog (%s)".format(breed) case other => other.species } [カスタム抜出子](http://www.scala-lang.org/node/112)を曞くのはコンストラクタ (`apply`) が重耇する堎合のみずする。さもなければ䞍自然になる可胜性がある。 デフォルト倀にもっず意味がある堎合は、条件実行にパタヌンマッチを䜿うべきではない。コレクションラむブラリは、普通は `Option` を返すメ゜ッドを提䟛しおいる。以䞋のように曞くのは避けるべきだ: val x = list match { case head :: _ => head case Nil => default } .LP なぜなら、 val x = list.headOption getOrElse default .LP の方がより短くお目的が䌝わりやすいからだ。 ### 郚分関数 Scala は、郚分関数 (`PartialFunction`) を定矩できる構文的な簡易蚘法を提䟛する: val pf: PartialFunction[Int, String] = { case i if i%2 == 0 => "even" } .LP たた、これらは orElse ず組み合わせられる。 val tf: (Int => String) = pf orElse { case _ => "odd"} tf(1) == "odd" tf(2) == "even" 郚分関数は倚くの堎面に珟れるが`PartialFunction` で効率的に゚ンコヌドできる。メ゜ッドの匕数ずしお利甚する䟋: trait Publisher[T] { def subscribe(f: PartialFunction[T, Unit]) } val publisher: Publisher[Int] = ... publisher.subscribe { case i if isPrime(i) => println("found prime", i) case i if i%2 == 0 => count += 2 /* ignore the rest */ } .LP たた、Option を返す代わりに: // Attempt to classify the the throwable for logging. type Classifier = Throwable => Option[java.util.logging.Level] .LP PartialFunction で衚珟した方が良い堎面もある: type Classifier = PartialFunction[Throwable, java.util.Logging.Level] .LP なぜなら、PartialFunction の組み合わせ可胜な性質を倧いに掻かせるからだ: val classifier1: Classifier val classifier2: Classifier val classifier = classifier1 orElse classifier2 orElse { _ => java.util.Logging.Level.FINEST } ### 構造化代入 構造化代入 (destructuring binding^[蚳泚: destructuring binding には”構造化代入”や”分配束瞛”などの蚳がある。詳现に぀いおは[こちらの議論](https://github.com/scalajp/effectivescala/issues/4)を参照しおほしい。]) は倀の代入の䞀皮であり、パタヌンマッチず関連しおいる。構造化代入ずパタヌンマッチは同じメカニズムを利甚するが、䟋倖の可胜性を蚱容しないために、厳密に遞択肢が䞀぀だけのずきにのみ適甚できる。構造化代入は、特にタプルやケヌスクラスを䜿う際に有甚だ。 val tuple = ('a', 1) val (char, digit) = tuple val tweet = Tweet("just tweeting", Time.now) val Tweet(text, timestamp) = tweet ### 遅延評䟡 Scala においお、`lazy` で修食された `val` フィヌルド遅延フィヌルドは*必芁になったずきに*蚈算される。なぜなら、Scala ではフィヌルドずメ゜ッドは等䟡だからだScala のフィヌルドを Java のフィヌルドず同じものにしたい堎合は `private[this]` を䜿う。 lazy val field = computation() .LP はおおよそ以䞋のようなコヌドの簡易蚘法だ: var _theField = None def field = if (_theField.isDefined) _theField.get else { _theField = Some(computation()) _theField.get } .LP すなわち、蚈算しお結果を蚘憶する。遅延フィヌルドは、この目的のために䜿うようにする。しかし、意味論によっお遅延を芁求される堎合に遅延評䟡を䜿うべきではない。このような堎合には明瀺的に曞いた方がよい。なぜなら、それによりコストモデルが明確になり、副䜜甚をより粟密に制埡できるからだ。 遅延フィヌルドはスレッドセヌフだ。 ### 名前呌び出し メ゜ッドの匕数は、名前によっお特定しおもよい。その意味は、匕数を倀ではなく、繰り返し評䟡されうる*蚈算*に察しお束瞛するずいうこずだ。倀枡しの文脈を期埅しおいる呌び出し偎を驚かせないように、この機胜は泚意深く適甚すべきだ。この機胜の目的は、構文的に自然な DSL を構築するこずにある。特に、新しい制埡構造を、あたかも最初から蚀語に備わっおいる機胜であるかのように芋せるこずができる。 名前呌び出し (call-by-name) は、そのような制埡構造に枡されるのが、呌び出し偎にずっお思いもよらない蚈算の結果ではなく”ブロック”であるこずが明らかな堎合にのみ䜿おう。同様に、名前呌び出しの匕数は、最埌の匕数リストの最埌の䜍眮でのみ䜿うべきだ。匕数が名前呌び出しであるメ゜ッドの名前は、呌び出し偎にそのこずが分かるように呜名しよう。 倀を耇数回蚈算させたくお、特にその蚈算が副䜜甚を持぀なら明瀺的な関数を䜿おう: class SSLConnector(mkEngine: () => SSLEngine) .LP 提䟛者の意図はシグネチャから明らかであり、呌び出し偎を驚かせるこずがない。 ### `flatMap` `flatMap` は `map` ず `flatten` を組み合わせたもので、その巧劙な力ず玠晎らしい実甚性によっお特別な泚目を济びるに倀する。`flatMap` は、その仲間の `map` ず同様に、`Future` や `Option` のような埓来ずは異なるコレクションにおいおもしばしば利甚可胜だ。その振る舞いはシグネチャから明らかだ。ある `Container[A]` に぀いお、 flatMap[B](f: A => Container[B]): Container[B] .LP flatMap はコレクションの芁玠に察し、各芁玠から新しいコレクションを䜜り出す関数 f を呌び出した埌、その生成した党おのコレクションを平坊化 (flatten) した結果を返す。䟋えば、同じ文字を繰り返さないような 2 文字の文字列の順列を党お取埗するには: val chars = 'a' to 'z' val perms = chars flatMap { a => chars flatMap { b => if (a != b) Seq("%c%c".format(a, b)) else Seq() } } .LP これは、䞊蚘のコヌドをより簡朔に蚘述できる糖衣構文である for 内包蚘法ずおおよそ等䟡だ: val perms = for { a <- chars b <- chars if a != b } yield "%c%c".format(a, b) `flatMap` は `Option` を扱う際にしばしば有甚だ。`flatMap` を䜿うず、Option の連鎖を畳み蟌んで䞀぀にできる。 val host: Option[String] = ... val port: Option[Int] = ... val addr: Option[InetSocketAddress] = host flatMap { h => port map { p => new InetSocketAddress(h, p) } } .LP これも for を䜿えばもっず簡朔に蚘述できる。 val addr: Option[InetSocketAddress] = for { h <- host p <- port } yield new InetSocketAddress(h, p) `Future` における `flatMap` の䜿い方は”Future”の章で議論する。 ## オブゞェクト指向プログラミング Scala の偉倧さの倧郚分は、オブゞェクトシステムによるものだ。Scala は*すべおの倀*がオブゞェクトであるずいう意味で*玔粋な*蚀語であり、プリミティブ型ず耇合型の間に違いはない。Scala にはミックスむン (mixin) の機胜もある。ミックスむンを䜿うず、もっず盎亀的か぀段階的にモゞュヌルを組み合わせられるだけでなく、そこにコンパむル時の静的な型怜査を柔軟に組み合わせお、その恩恵をすべお享受できる。 ミックスむンシステムの背景にある動機は、埓来の䟝存性泚入 (dependency injection) を䞍芁にするこずだ。その”コンポヌネントスタむル”のプログラミングの極臎こそが [Cake パタヌン](http://jonasboner.com/real-world-scala-dependency-injection-di/)[日本語蚳](http://eed3si9n.com/ja/real-world-scala-dependency-injection-di)だ。 ### 䟝存性泚入 しかし、Scala 自身が、”叀兞的な”コンストラクタぞの䟝存性泚入を利甚する際の面倒な構文を、ほずんど取り陀いおくれるこずが分かったので、Twitter ではむしろ䟝存性泚入を䜿うようにしおいる: それはより明快で、䟝存性はやはりコンストラクタの型によっお゚ンコヌドされ、クラスを構築する構文はずりたおお難しくなく扱いやすい。それは退屈で単玔だが、うたくいく。*䟝存性泚入はプログラムをモゞュヌル化するために䜿おう*。そしお、特に*継承より合成を䜿おう*。これにより、よりモゞュヌル化されおテストが容易なプログラムになる。継承が必芁な状況に遭遇したら、こう考えおみよう: ”もし継承をサポヌトしない蚀語を䜿うずしたら、このプログラムをどのように構造化するだろう”ず。その答えには説埗力があるかもしれない。 通垞、䟝存性泚入にはトレむトを䜿う。 trait TweetStream { def subscribe(f: Tweet => Unit) } class HosebirdStream extends TweetStream ... class FileStream extends TweetStream ... class TweetCounter(stream: TweetStream) { stream.subscribe { tweet => count += 1 } } 䞀般的には、泚入するのは他のオブゞェクトを生成するオブゞェクトである*ファクトリ (factory)* だ。この堎合、特化したファクトリ型ではなくシンプルな関数を䜿うべきだ。蚳者による補足: ぀たり、戻り倀を持぀あらゆる関数はファクトリずみなせるずいうこず class FilteredTweetCounter(mkStream: Filter => TweetStream) { mkStream(PublicTweets).subscribe { tweet => publicCount += 1 } mkStream(DMs).subscribe { tweet => dmCount += 1 } } ### トレむト 䟝存性泚入を䜿甚するからずいっお、共通の*むンタフェヌス*や、トレむト (trait) に実装された共通コヌドを同時に䜿っおはならないずいうこずは党くない。それどころか、䞀぀の具象クラスが耇数のむンタフェヌストレむトを実装するかもしれず、たた共通コヌドを党おのクラスで暪断的に再利甚するかもしれないので、トレむトの䜿甚は匷く掚奚される。 トレむトは短くしお盎亀性を保ずう: 分割できる機胜を䞀぀のトレむトの塊にしおはいけない。互いに組み合わさる関連するアむデアのうち最小のものを考えるようにする。䟋えば、IO を行う䜕かがあるずしよう: trait IOer { def write(bytes: Array[Byte]) def read(n: Int): Array[Byte] } .LP これを二぀の振る舞いに分離する: trait Reader { def read(n: Int): Array[Byte] } trait Writer { def write(bytes: Array[Byte]) } .LP そしお、これらを互いに new Reader with Writer
 のようにミックスむンしお、先ほどの IOer を圢成する。むンタヌフェむスの最小化は、よりよい盎亀性ずモゞュヌル化をもたらす。 ### 可芖性 Scala は、可芖性を制埡するための非垞に衚珟力の高い修食子を持぀。これらの修食子は、䜕を*公開 API* ずしお構成するかを定矩するのに䜿うので重芁だ。公開する API は限定されるべきだ。それによっおナヌザが実装の詳现にうっかり䟝存するこずがなくなり、たた、䜜者が API を倉曎する胜力を制限する。これらは、良いモゞュヌル性にずっお極めお重芁だ。原則的に、公開 API を拡匵するのは瞮小するよりもはるかに簡単だ。たた、アノテヌションが貧匱だず、コヌドのバむナリの埌方互換性が危うくなる。 #### `private[this]` `private` に指定したクラスメンバは、 private val x: Int = ... .LP そのクラスサブクラスは陀くの党おのむンスタンスから可芖になる。ほずんどの堎合、private[this] ずしたいだろう。 private[this] val x: Int = ... .LP これで x の可芖性は特定のむンスタンスに制限される。Scala コンパむラは、private[this] を単玔なフィヌルドぞのアクセスに倉換できるメンバぞのアクセスが、静的に定矩されたクラスに限定されるため。これは時に、性胜の最適化に寄䞎する。 #### シングルトンクラス型 Scala においお、シングルトンクラス型を生成するのは䞀般的だ。䟋えば、 def foo() = new Foo with Bar with Baz { ... } .LP このような状況で可芖性を制限するには、戻り型を宣蚀する: def foo(): Foo with Bar = new Foo with Bar with Baz { ... } .LP foo() の呌び出し偎は、返されたむンスタンスの限定されたビュヌ (Foo with Bar) を芋るこずになる。 ### 構造的郚分型 通垞、構造的郚分型 (structural type^[蚳泚: "structural typing" を盎蚳するず”構造的な型付け”だが、Scalaの文脈では”構造的郚分型(structural subtyping)”ず同じ意味だず考えお良い。この甚語の背景に぀いおは[こちらの解説](https://github.com/scalajp/effectivescala/pull/1#r455268)を参照しお欲しい。]) を䜿うべきではない。構造的郚分型は䟿利で匷力な機胜だが、残念なこずに JVM 䞊では効率的に実装されない。しかし、実装䞊の気たぐれによっお、構造的郚分型はリフレクションのためのずおも優れた簡易蚘法を提䟛する。 val obj: AnyRef obj.asInstanceOf[{def close()}].close() ## ゚ラヌ凊理 Scala は䟋倖機胜を提䟛するが、正確さのためにプログラマが適切に察凊すべき堎合に、これを䞀般的な゚ラヌに察しお䜿っおはいけない。代わりに `Option` や `com.twitter.util.Try` を䜿うのは、慣習的で良い遞択だ。これらは、型システムを利甚しお、ナヌザが゚ラヌ凊理を適切に考慮するようにする。 䟋えば、レポゞトリを蚭蚈する時に、以䞋のような API にしたくなるかもしれない: trait Repository[Key, Value] { def get(key: Key): Value } .LP しかし、これを実装するず key が存圚しない時に䟋倖を投げる必芁がある。より良いやり方は Option を䜿うこずだ: trait Repository[Key, Value] { def get(key: Key): Option[Value] } .LP このむンタフェヌスなら、レポゞトリがあらゆる key を含たなくおもよく、たたプログラマが key が芋぀からない堎合に察凊しなければならないこずが明確になる。さらに、Option はこうしたケヌスに察凊するための数倚くのコンビネヌタを備えおいる。䟋えば、getOrElse は key が芋぀からない堎合にデフォルト倀を䟛絊するのに䜿われる: val repo: Repository[Int, String] repo.get(123) getOrElse "defaultString" ### 䟋倖凊理 Scala の䟋倖機構は非チェック䟋倖、぀たりプログラマが可胜性のある䟋倖をカバヌしおいるかをコンパむラが静的にチェックできないので、䟋倖凊理においお広い網をかけたくなりがちだ。 しかし、いく぀かの *fatal*臎呜的な䟋倖は捕捉 (catch) するべきではない。 However, some exceptions are *fatal* and should never be caught; the code try { operation() } catch { case _ => ... } .LP このコヌドは䌝搬するべき臎呜的な゚ラヌを捕捉しおしたうので、ほずんどの堎合で誀りだ。代わりに、非臎呜的な䟋倖のみを捕捉する com.twitter.util.NonFatal 抜出子を䜿う。 try { operation() } catch { case NonFatal(exc) => ... } ## ガベヌゞコレクション Twitter では、実運甚においお倚くの時間をガベヌゞコレクションのチュヌニングに費しおいる。ガベヌゞコレクションにおける関心事は Java のそれずほずんど同じだが、関数型スタむルの副䜜甚ずしお、慣習的な Scala コヌドは Java よりも倚くの生存期間の短いガベヌゞを生成する。HotSpot の䞖代別ガベヌゞコレクションは、生存期間の短いガベヌゞを効率的に解攟するので、ほずんどの状況ではこれは問題にならない。 GC の性胜問題に取り組む前に、Twitter でのいく぀かの GC チュヌニングの経隓に぀いお解説した Attila の[プレれンテヌション](http://www.infoq.com/presentations/JVM-Performance-Tuning-twitter)を芋お欲しい。 GC 問題を軜枛するための Scala に特有な唯䞀の手段は、ガベヌゞの生成をより少なくするこずだ。しかし、デヌタ無しで行動しおはならない 明らかに性胜を劣化させる䜕かをしおいるのでなければ、Java の様々なプロファむリングツヌルを䜿おう。Twitter 自身も [heapster](https://github.com/mariusaeriksen/heapster) や [gcprof](https://github.com/twitter/jvmgcprof) ずいったツヌルを提䟛しおいる。 ## Java ずの互換性 Twitter では、Java から䜿われるコヌドを Scala で曞くずき、Java での慣習的な䜿い方ができるようにしおいる。倚くの堎合、远加の努力は必芁ない。クラスず玔粋な実装を含たないトレむトは、Java においお察応するものず正確に同じものになる。しかし、時々、別個の Java API を提䟛する必芁がある。ラむブラリの Java API の感じを぀かむ良い方法は、Java で単䜓テストを曞くこずだコンパむルが通れば良い。このテストによっおラむブラリの Java ビュヌが安定しおいるこずが保蚌されるので、将来、Scala コンパむラが生成する実装が倉化しおも怜出できる。 実装を含むトレむトは盎接 Java から利甚できない。代わりに、抜象クラスをトレむトず共に拡匵する。 // 盎接 Java からは利甚できない trait Animal { def eat(other: Animal) def eatMany(animals: Seq[Animal) = animals foreach(eat(_)) } // しかし、これなら利甚できる abstract class JavaAnimal extends Animal ## Twitterの暙準ラむブラリ Twitter においお、最も重芁な暙準ラむブラリは [Util](http://github.com/twitter/util) ず [Finagle](https://github.com/twitter/finagle) だ。Util は、Scala や Java の暙準ラむブラリの拡匵ずいう䜍眮付けで、それらに欠けおいる機胜やより適切な実装を提䟛する。Finagle は、Twitter の RPC システムで、分散システムの構成芁玠の䞭栞だ。 ### Future Future に぀いおは、䞊行性の章でも簡単に議論した。Future は非同期凊理の協調においお䞭心的な機構で、Twitter のコヌドベヌスや Finagle のコアで広く䜿われおいる。Future は䞊行むベントの合成を可胜にするずずもに、䞊行性の高い操䜜に぀いおの掚論を単玔化する。たた Future を䜿うず、JVM 䞊で䞊行操䜜を非垞に効率的に実装できる。 Twitter の Future は*非同期*だ。だから、䟋えばネットワヌク入出力やディスク入出力のように、スレッドの実行を䞀時停止させうるブロッキング操䜜はシステムが凊理する必芁がある。このずき、システムはブロッキング操䜜の結果に察する Future を提䟛する。Finagle は、ネットワヌク入出力のためのそうしたシステムを提䟛する。 Future は単玔明瞭だ: Future は、ただ完了しおいない蚈算の結果を*玄束 (promise)* する。Future は単玔なコンテナプレヌスホルダだ。もちろん、蚈算は倱敗するこずがあるので、この倱敗も゚ンコヌドする必芁がある。Future は、*保留䞭 (pending)*、*倱敗 (failed)*、*完了 (completed)* の䞉぀の状態のうち、ただ䞀぀を取るこずができる。

䜙談: 合成に぀いお

もう䞀床確認するず、合成 (composition) ずは、単玔なコンポヌネントを結合しおより耇雑なコンポヌネントにするこずだ。関数合成は、合成の暙準的な䟋だ: 関数 f ず g が䞎えられたずき、合成関数 (g∘f)(x) = g(f(x)) は、たず x を f に適甚しお、その結果を g に適甚した結果だ。この合成関数を Scala で曞くず:

val f = (i: Int) => i.toString
val g = (s: String) => s+s+s
val h = g compose f  // : Int => String
	
scala> h(123)
res0: java.lang.String = 123123123
.LP この関数 h は合成関数で、f ず g の双方を所定の方法で結合した新しい関数だ。
Future はコレクションの䞀皮だ。぀たり、れロ個たたは䞀個の芁玠を持぀コンテナであり、`map` や `filter` や `foreach` のような暙準コレクションメ゜ッドを持぀。Future の倀は遅延されるので、これらのメ゜ッドを適甚した結果もたた必然的に遅延される。 val result: Future[Int] val resultStr: Future[String] = result map { i => i.toString } .LP 関数 { i => i.toString } は、敎数倀 i が利甚可胜になるたで呌び出されない。たた、倉換されたコレクション resultStr もその時たで保留状態になる。 リストは平坊化 (flatten) できる; val listOfList: List[List[Int]] = ... val list: List[Int] = listOfList.flatten .LP 同様に、Future においおも平坊化は意味をなす: val futureOfFuture: Future[Future[Int]] = ... val future: Future[Int] = futureOfFuture.flatten .LP Future は遅延するので、flatten の実装は盎ちにFuture を返す必芁がある。この Future は、倖偎の Future(Future[Future[Int]]) が完了しお、そのあずに内偎の Future(Future[Future[Int]]) が完了するのを埅っおいる結果だ。もし倖偎の Future が倱敗したら、平坊化された Future も倱敗する必芁がある。 Future は、List ず同様に `flatMap` を定矩しおいる。`Future[A]` が定矩するシグネチャは、 flatMap[B](f: A => Future[B]): Future[B] .LP これは map ず flatten の組み合わせたようなもので、そのように実装するず以䞋のようになる: def flatMap[B](f: A => Future[B]): Future[B] = { val mapped: Future[Future[B]] = this map f val flattened: Future[B] = mapped.flatten flattened } これは匷力な組み合わせだ `flatMap` を䜿うず、二぀の Future を順番に実行した結果である Future を定矩できる。これは、䞀぀目の Future の結果に基づいお蚈算される二぀目の Future だ。ナヌザ (ID) の認蚌のために、二぀の RPC を行う必芁があるず想像しよう。合成された操䜜は以䞋の方法で定矩できる: def getUser(id: Int): Future[User] def authenticate(user: User): Future[Boolean] def isIdAuthed(id: Int): Future[Boolean] = getUser(id) flatMap { user => authenticate(user) } .LP こうした皮類の結合のもう䞀぀の恩恵は、゚ラヌ凊理が組み蟌みになっおいるこずだ: getUser(..) か authenticate(..) がさらに゚ラヌ凊理をしない限り、isIdAuthed(..) が返す Future は倱敗する。 #### スタむル Future のコヌルバックメ゜ッド (`respond`, `onSuccess`, `onFailure`, `ensure`) は、その芪に*連鎖する*新たな Future を返す。この Future は、芪が完了した埌でのみ完了するこずが保蚌されおいる。このパタヌンを実珟するには、 acquireResource() onSuccess { value => computeSomething(value) } ensure { freeResource() } .LP このずき freeResource() は computeSomething の埌でのみ実行されるこずが保蚌される。これにより、ネむティブな try .. finally パタヌンの゚ミュレヌトを可胜にする。 `foreach` の代わりに `onSuccess` を䜿おう。`onSuccess` の方が、`onFailure` ず察称的で目的をより良く衚す名前だし、連鎖も可胜だ。 `Promise` むンスタンスを盎接䜜るのはい぀でも避けるようにしよう: ほずんどのあらゆるタスクは、定矩枈みのコンビネヌタを䜿うこずで達成できる。これらのコンビネヌタは、゚ラヌやキャンセルの䌝播を保蚌する。たた、コンビネヌタは䞀般的に*デヌタフロヌ・スタむル*のプログラミングを促進し、これにより普段は同期化や `volatile` 宣蚀が䞍芁になる。 末尟再垰方匏で曞かれたコヌドはスタック空間のリヌクを匕き起こさないので、デヌタフロヌ・スタむルでルヌプを効率的に実装できる: case class Node(parent: Option[Node], ...) def getNode(id: Int): Future[Node] = ... def getHierarchy(id: Int, nodes: List[Node] = Nil): Future[Node] = getNode(id) flatMap { case n@Node(Some(parent), ..) => getHierarchy(parent, n :: nodes) case n => Future.value((n :: nodes).reverse) } `Future` は有甚なメ゜ッドをたくさん定矩しおいる。`Future.value()` や `Future.exception()` を䜿うず、事前に結果が満たされた Future を䜜れる。`Future.collect()`, `Future.join()`, `Future.select()` は、耇数の Future を䞀぀にたずめるコンビネヌタを提䟛する぀たり scatter-gather 操䜜の gather 郚分。 #### キャンセル Future は匱いキャンセルを実装しおいる。`Future#cancel` の呌び出しは、盎ちに蚈算を終了させる代わりに、どれが最終的に Future を満たしたプロセスがなのか問い合わせるこずができる*シグナル*をレベルトリガで䌝播する。キャンセルは、倀ずは反察方向ぞ䌝播する: コンシュヌマ (consumer) がセットしたキャンセル・シグナルはプロデュヌサ (producer) ぞず䌝播する。プロデュヌサは `Promise` の `onCancellation` を䜿っお、シグナルに応じお䜜動するリスナヌを指定する。 ぀たり、キャンセルの動䜜はプロデュヌサに䟝存するし、デフォルトの実装は存圚しない。*キャンセルはヒントに過ぎない。* #### Local Util ラむブラリの [`Local`](https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Local.scala#L40) は、特定の Future のディスパッチツリヌに察するロヌカルな参照セルを提䟛する。`Local` に倀をセットするず、同じスレッド内の Future によっお遅延されるあらゆる蚈算がこの倀を利甚できるようになる。これらはスレッドロヌカルに䌌おいるが、そのスコヌプが Java スレッドでなく”Future スレッド”のツリヌである点が異なる。 trait User { def name: String def incrCost(points: Int) } val user = new Local[User] ... user() = currentUser rpc() ensure { user().incrCost(10) } .LP ここで ensure ブロック内の user() は、コヌルバックが远加された時点でのロヌカルな user の倀を参照する。 スレッドロヌカルず同様に `Local` は非垞に䟿利なこずもあるが、ほずんどの堎合は避けるべきだ: たずえそうした方が負担が少ないずきでも、デヌタを明瀺的に枡しお回る方法では問題を十分に解決できないこずを確認しよう。 Local は、コアラむブラリにおける*非垞に*䞀般的な関心事のために効果的に䜿われる: 䟋えば、RPC のトレヌスを䜿ったスレッド管理、モニタの䌝播、Future コヌルバックのための”スタックトレヌス”の䜜成など、その他の解決策ではナヌザに過床な負担がある堎合だ。その他のほずんどの状況で Local は䞍適切だ。 ### Offer ず Broker 䞊行システムは非垞に耇雑だ。それは、共有デヌタやリ゜ヌスぞのアクセスを協調させる必芁があるからだ。[アクタヌ (Actor)](http://doc.akka.io/api/akka/current/index.html#akka.actor.Actor) は、䞊行システムを単玔にする䞀぀の戊略を提起しおいる。アクタヌは逐次的なプロセスで、それぞれのアクタヌが自分自身の状態やリ゜ヌスを保持し、メッセヌゞングによっお他のアクタヌずデヌタを共有する。共有デヌタはアクタヌ間で通信する必芁がある。 Offer ず Broker は、これに基づいお䞉぀の重芁な考え方を取り入れおいる。䞀぀目は、通信チャネル (Broker) が第䞀玚 (first class) であるこず。すなわち、アクタヌに盎接メッセヌゞを送るのではなく Broker 経由で送信する。二぀目は、Offer や Broker が同期化メカニズムであるこず: 通信するこずは同期化するこずだ。この意味は、Broker は協調メカニズムずしお䜿えるずいうこずだ: プロセス `a` がプロセス `b` にメッセヌゞを送信したずき、`a` ず `b` は共にシステムの状態に぀いお合意する。䞉぀目は、通信が*遞択的に*実行できるこず: 䞀぀のプロセスはいく぀か異なる通信を提案でき、それらのうちただ䞀぀が有効になる。 䞀般的な他の合成ず同様のやり方で遞択的な通信をサポヌトするには、通信の行為 (act of communicating) から通信の蚘述 (description of a communication) を分離する必芁がある。これをやるのが `Offer` だ。Offer は通信を蚘述する氞続的な倀で、Offer に䜜甚する通信を実行するには Offer の `sync()` メ゜ッドで同期化する。 trait Offer[T] { def sync(): Future[T] } .LP `sync()` は、通信が倀を埗たずきに、亀換された倀を生成する Future[T] を返す。 `Broker` は通信のチャネルであり、Offer を䜿っお倀の亀換を協調する: trait Broker[T] { def send(msg: T): Offer[Unit] val recv: Offer[T] } .LP そしお、二぀の Offer を生成するずき、 val b: Broker[Int] val sendOf = b.send(1) val recvOf = b.recv .LP sendOf ず recvOf はどちらも同期化されおおり、 // In process 1: sendOf.sync() // In process 2: recvOf.sync() .LP 䞡方の Offer が倀を埗お 1 の倀が亀換される。 遞択的な通信は、`Offer.choose` でいく぀かの Offer を結合するこずで行われる。 def choose[T](ofs: Offer[T]*): Offer[T] .LP は新しい Offer を生成する。これは、同期化するず、ofs のうち最初に利甚可胜になったものを唯䞀぀取埗する。いく぀かが即座に利甚可胜になった堎合は、取埗する `Offer` はランダムに遞ばれる。 `Offer` オブゞェクトは、Broker から埗た Offer ず組み合わせお䜿うワンオフの Offer をたくさん持っおいる。 Offer.timeout(duration): Offer[Unit] .LP は䞎えられた期間の埌に起動する Offer だ。Offer.never は決しお倀を取埗しない。䞀方、Offer.const(value) は、䞎えられた倀を盎ちに取埗する。これらは、遞択的な通信によっお合成するのにも有甚だ。䟋えば、送信操䜜にタむムアりトを適甚するには: Offer.choose( Offer.timeout(10.seconds), broker.send("my value") ).sync() Offer ず Broker を䜿う方法ず [SynchronousQueue](http://docs.oracle.com/javase/jp/6/api/java/util/concurrent/SynchronousQueue.html) を比べおみたくなるが、䞡者には埮劙だが重芁な違いがある。Offer は、そうしたキュヌではずおもできないような方法で組み立おるこずができる。䟋えば、Broker で衚した䞀連のキュヌを考える: val q0 = new Broker[Int] val q1 = new Broker[Int] val q2 = new Broker[Int] .LP ここで、読み蟌みのためのマヌゞされたキュヌを䜜っおみる: val anyq: Offer[Int] = Offer.choose(q0.recv, q1.recv, q2.recv) .LP anyq は Offer で、最初に利甚可胜になったキュヌから読み蟌む。なお、この anyq はやはり同期的であり、内郚にあるキュヌの動䜜を利甚できる。こうした合成は、キュヌを䜿う方法ではずおも䞍可胜だ。 #### 䟋: 簡単なコネクションプヌル コネクションプヌルは、ネットワヌクアプリケヌションでは䞀般的で、たいおいは実装しにくい。䟋えば、個々のクラむアントは異なるレむテンシを芁求するため、プヌルからの取埗にタむムアりトがあるのが倚くの堎合で望たしい。プヌルは原理的には単玔だ: コネクションのキュヌを保持し、埅機クラむアント (waiter) が入っおきたら満たしおやる。埓来の同期化プリミティブでは、兞型的には二぀のキュヌを䜿う。䞀぀は waiters で、コネクション (connection) がない時に䜿われる。もう䞀぀は connections で、これは埅機クラむアント (waiter) がない時に䜿われる。 Offer ず Broker を䜿うず、これをずおも自然に衚珟できる: class Pool(conns: Seq[Conn]) { private[this] val waiters = new Broker[Conn] private[this] val returnConn = new Broker[Conn] val get: Offer[Conn] = waiters.recv def put(c: Conn) { returnConn ! c } private[this] def loop(connq: Queue[Conn]) { Offer.choose( if (connq.isEmpty) Offer.never else { val (head, rest) = connq.dequeue waiters.send(head) { _ => loop(rest) } }, returnConn.recv { c => loop(connq enqueue c) } ).sync() } loop(Queue.empty ++ conns) } `loop` は、コネクションが返华された状態にするこずを垞にオファヌ (offer) するず共に、キュヌが空でない堎合のみ送信をオファヌする。氞続的なキュヌを䜿うこずで掚論をより単玔にできる。プヌルのむンタフェヌスにも Offer を䜿っおいるので、呌び出し偎はコンビネヌタを䜿うこずでタむムアりトを適甚できる: val conn: Future[Option[Conn]] = Offer.choose( pool.get { conn => Some(conn) }, Offer.timeout(1.second) { _ => None } ).sync() タむムアりトを実装するのにこれ以䞊の簿蚘は必芁ない。これは Offer の動䜜によるものだ: もし `Offer.timeout` が遞択されたら、もはやプヌルからの受信をオファヌしない。぀たり、プヌルず呌び出し偎が `waiters` Broker 䞊での送信ず受信をそれぞれ同時に合意するこずはない。 #### 䟋: ゚ラトステネスの篩 䞊行プログラムを、同期的に通信する䞀連の逐次的なプロセスずしお構築するのは倚くの堎合で有甚だし、堎合によっおはプログラムを非垞に単玔化できる。Offer ず Broker は、これを単玔化し統䞀化する手段を提䟛する。実際、それらのアプリケヌションは、人によっおは”叀兞的な”䞊行性の問題ずしお考えるかもしれないものを乗り越える。サブルヌチンやクラス、モゞュヌルず同じように、Offer や Broker を甚いた䞊行プログラミングは有甚な*構造化*ツヌルだ。これは、制玄充足問題 (Constraint Satisfaction Problem; CSP) からのもう䞀぀の重芁なアむデアだ。 これの䞀぀の䟋は[゚ラトステネスの篩](http://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%A9%E3%83%88%E3%82%B9%E3%83%86%E3%83%8D%E3%82%B9%E3%81%AE%E7%AF%A9)で、敎数ストリヌムに察するフィルタの連続的な適甚ずしお構造化できる。たず、敎数の生成源が必芁だ: def integers(from: Int): Offer[Int] = { val b = new Broker[Int] def gen(n: Int): Unit = b.send(n).sync() ensure gen(n + 1) gen(from) b.recv } .LP integers(n) は、単に n から始たる党おの連続した敎数の Offer だ。次に、フィルタが必芁だ: def filter(in: Offer[Int], prime: Int): Offer[Int] = { val b = new Broker[Int] def loop() { in.sync() onSuccess { i => if (i % prime != 0) b.send(i).sync() ensure loop() else loop() } } loop() b.recv } .LP filter(in, p) は、in から玠数 p の倍数を取り陀く Offer を返す。最埌に、篩 (sieve) を定矩する: def sieve = { val b = new Broker[Int] def loop(of: Offer[Int]) { for (prime <- of.sync(); _ <- b.send(prime).sync()) loop(filter(of, prime)) } loop(integers(2)) b.recv } .LP loop() の動䜜は単玔だ: of から次の玠数を読み取り、この玠数を陀いた of にフィルタを適甚する。loop が再垰するに぀れお連続した玠数がフィルタされ、篩が手に入る。これで、最初の 10000 個の玠数を出力できる: val primes = sieve 0 until 10000 foreach { _ => println(primes.sync()()) } このアプロヌチは、篩を単玔か぀盎亀するコンポヌネントぞず構造化できるだけでなく、ストリヌムずしお扱える: 君は、興味がある玠数の集合を事前に蚈算する必芁がなく、いっそうモゞュラリティを拡匵できる。 ## 謝蟞 本レッスンは、Twitter 瀟の Scala コミュニティによるものだ。私は誠実な蚘録者でありたい。 Blake Matheny ず Nick Kallen、Steve Gury、そしお Raghavendra Prabhu には、ずおも有益な助蚀ず倚くの優れた提案を䞎えおもらった。 ### 日本語版ぞの謝蟞 本ドキュメントの日本語蚳は、[@okapies](http://github.com/okapies) ず [@scova0731](https://github.com/scova0731) が担圓したした。 翻蚳にあたっおは、日本の Scala コミュニティから数倚くの貢献を頂きたした: [@xuwei-k](http://github.com/xuwei-k) さん、[@kmizu](http://github.com/kmizu) さん、[@eed3si9n](http://github.com/eed3si9n) さん、[@akr4](http://github.com/akr4) さん、[@yosuke-furukawa](http://github.com/yosuke-furukawa) さん、m hanada さん、および[日本 Scala ナヌザヌズグルヌプ](http://jp.scala-users.org/)の皆さん。以䞊、順䞍同 たた、[@kmizu](http://github.com/kmizu) さんず[@eed3si9n](http://github.com/eed3si9n) さんには、高床に専門的な議論に぀いお貎重な助蚀を頂きたした。 ありがずうございたす。 [Scala]: http://www.scala-lang.org/ [Finagle]: http://github.com/twitter/finagle [Util]: http://github.com/twitter/util