Skip to content
This repository

update Japanese edition. #13

Merged
merged 2 commits into from almost 2 years ago

2 participants

Yuta Okamoto marius a. eriksen
Yuta Okamoto

Update 'effectivescala-ja.mo' to follow revision of the original, including the section of 'Offer/Broker'. Please merge it.

marius a. eriksen mariusaeriksen merged commit 7bbf3db into from
marius a. eriksen
Collaborator

Thanks! Sorry it took me a while — I didn’t see it until now.

Yuta Okamoto

No problem. Thanks, marius!
Your presentation about offer/broker is also helpful for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Jun 20, 2012
Yuta Okamoto okapies apply trivial changes in the original. f0a0c86
Jun 25, 2012
Yuta Okamoto okapies translate section of 'Offer/Broker'. 2c904d7
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 157 additions and 12 deletions. Show diff stats Hide diff stats

  1. +157 12 effectivescala-ja.mo
169 effectivescala-ja.mo
... ... @@ -1,4 +1,4 @@
1   -<a href="http://github.com/twitter/effectivescala"><img style="position: absolute; top: 0; left: 0; border: 0;" src="https://a248.e.akamai.net/assets.github.com/img/edc6dae7a1079163caf7f17c60495bbb6d027c93/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f6c6566745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub"></a>
  1 +<a href="http://github.com/twitter/effectivescala"><img style="position: absolute; top: 0; left: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_left_green_007200.png" alt="Fork me on GitHub"></a>
2 2
3 3 <h1 class="header">Effective Scala</h1>
4 4 <address>Marius Eriksen, Twitter Inc.<br />marius@twitter.com (<a href="http://twitter.com/marius">@marius</a>)<br /><br />[translated by Yuta Okamoto(<a href="http://github.com/okapies">@okapies</a>) and Satoshi Kobayashi(<a href="https://github.com/scova0731">@scova0731</a>)]</address>
@@ -233,7 +233,7 @@ Scalaでは戻り型アノテーション(return type annotation)を省略でき
233 233
234 234 エイリアスが使える場合はサブクラス化を使ってはいけない。
235 235
236   - trait SocketFactory extends (SocketAddress) => Socket
  236 + trait SocketFactory extends (SocketAddress => Socket)
237 237
238 238 .LP <code>SocketFactory</code>は、<code>Socket</code>を生成する<em>関数</em>だ。型エイリアス
239 239
@@ -601,7 +601,7 @@ elaborate..
601 601
602 602 ## 関数型プログラミング
603 603
604   -関数型プログラミングと一緒に用いる時に *値指向型* プログラミングは多くの恩恵を受ける。このスタイルはステートフルな変更よりも値の変換を強調する。得られるコードは参照透過(referentially transparent)であり、より強力な不変式(invariant)を提供し、さらに容易に推論することが可能になる。ケースクラス、パターンマッチ、構造化代入(destructuring-bind)、型推論、軽量クロージャ、メソッド生成構文がこのツールになる。
  604 +関数型プログラミングと一緒に用いる時に *値指向型* プログラミングは多くの恩恵を受ける。このスタイルはステートフルな変更よりも値の変換を強調する。得られるコードは参照透過(referentially transparent)であり、より強力な不変式(invariant)を提供し、さらに容易に推論することが可能になる。ケースクラス、パターンマッチ、構造化代入(destructuring-bind)、型推論、クロージャやメソッドの軽量な生成構文がこのツールになる。
605 605
606 606 ### 代数的データ型としてのケースクラス
607 607
@@ -1058,7 +1058,7 @@ Futureは、Listと同様に`flatMap`を定義している。`Future[A]`は、
1058 1058
1059 1059 #### スタイル
1060 1060
1061   -Futureのコールバックメソッドである`respond`や`onSuccess'、`onFailure`、`ensure`は、その親に*連鎖した(chained)*新たなFutureを返す。このFutureは、その親が完了して初めて完了することが保証されている。このパターンを実現するには、例えば以下のようにする。
  1061 +Futureのコールバックメソッドである`respond`や`onSuccess`、`onFailure`、`ensure`は、その親に*連鎖した(chained)*新たなFutureを返す。このFutureは、その親が完了して初めて完了することが保証されている。このパターンを実現するには、例えば以下のようにする。
1062 1062
1063 1063 acquireResource()
1064 1064 future onSuccess { value =>
@@ -1071,9 +1071,9 @@ Futureのコールバックメソッドである`respond`や`onSuccess'、`onFai
1071 1071
1072 1072 `foreach`の代わりに`onSuccess`を使おう。`onSuccess`の方が`onFailure`と対称を成して目的をより良く表せるし、連鎖も可能になる。
1073 1073
1074   -できるだけ自分で`Promise`を作らないようにしよう。ほぼ全てのタスクは、定義済みの結合子を使って実現できる。結合子は、エラーやキャンセルが伝播することを保証すると共に、一般的に*データフロー方式*でのプログラミングを促進する。データフロー方式を使うと、大抵、<a href="#並行性-Future">同期化や`volatile`宣言が不要になる</a>。
  1074 +なるべく、`Promise`インスタンスを直接作らないようにしよう。ほぼ全てのタスクは、定義済みの結合子を使って実現できる。結合子は、エラーやキャンセルが伝播することを保証すると共に、一般的に*データフロー方式*でのプログラミングを促進する。データフロー方式を使うと、大抵、<a href="#並行性-Future">同期化や`volatile`宣言が不要になる</a>。
1075 1075
1076   -末尾再帰方式で書かれたコードはスペースリークに影響されないので、データフロー方式を使ってループを効率的に実装できる:
  1076 +末尾再帰方式で書かれたコードは、スタック空間のリークを引き起こさないので、データフロー方式を使ってループを効率的に実装できる:
1077 1077
1078 1078 case class Node(parent: Option[Node], ...)
1079 1079 def getNode(id: Int): Future[Node] = ...
@@ -1086,8 +1086,6 @@ Futureのコールバックメソッドである`respond`や`onSuccess'、`onFai
1086 1086
1087 1087 `Future`は、数多くの有用なメソッドを定義している。`Future.value()`や`Future.exception()`を使うと、事前に結果が満たされたFutureを作れる。`Future.collect()`や`Future.join()`、`Future.select()`は、複数のFutureを一つにまとめる結合子を提供する(ie. scatter-gather操作のgather部分)。
1088 1088
1089   -(訳注: スペースリーク(space leak)とは、意図せずに空間計算量が非常に大きいコードを書いてしまうこと。関数型プログラミングでは、遅延評価式を未評価のまま蓄積するようなコードを書くと起きやすい。)
1090   -
1091 1089 #### キャンセル
1092 1090
1093 1091 Futureは、弱いキャンセルを実装している。`Future#cancel`の呼び出しは、計算を直ちに終了させるのではなく、レベルトリガ方式の*シグナル*を伝播する。最終的にFutureを満たすのがいずれの処理であっても、シグナルに問い合わせる(query)ことができる。キャンセルは、値から反対方向へ伝播する。つまり、消費者(consumer)がセットしたキャンセルのシグナルは、対応する生産者(producer)へと伝播する。生産者は`Promise`にある`onCancellation`を使って、シグナルに応じて作動するリスナーを指定する。
@@ -1117,16 +1115,163 @@ Utilライブラリの[`Local`](https://github.com/twitter/util/blob/master/util
1117 1115
1118 1116 Localは、RPCのトレースを介したスレッド管理や、モニターの伝播、Futureコールバックのための"スタックトレース"の作成など、*とても*一般的な関心事(concern)を実現する際に、その他の解決策ではユーザに過度な負担がある場合、コアとなるライブラリにおいて効果的に使われる。Localは、その他のほとんどの場面では不適切だ。
1119 1117
1120   -<!--
1121   - ### Offer/Broker
  1118 +### OfferとBroker
1122 1119
1123   --->
  1120 +並行システムは非常に複雑だ。それは、共有データやリソースへのアクセスを協調させる必要があるからだ。[Actor](http://www.scala-lang.org/api/current/scala/actors/Actor.html)は、単純化の一つの戦略を提起している。Actorはシーケンシャルなプロセスで、それぞれが自分自身の状態やリソースを保持している。そして、データは、他のActorとのメッセージングによって共有される。共有データは、Actor間で通信する必要がある。
  1121 +
  1122 +OfferとBrokerは、これに基づいて、三つの重要な考え方を取り入れている。一つ目、通信チャネル(Broker)は第一級(first class)だ。すなわち、Actorに直接メッセージを送るのではなく、Broker経由で送信する。二つ目、OfferやBrokerは同期化メカニズムであり、通信することは同期化することだ。これは、Brokerが協調メカニズムとして使えることを意味する。プロセス`a`がプロセス`b`にメッセージを送信したとき、`a`と`b`の両方とも、システムの状態について合意する。三つ目、通信は*選択的に*実行できる。一つのプロセスは、いくつかの異なる通信を提案でき、それらのうち、ただ一つが有効になる。
  1123 +
  1124 +一般的な(他の合成と同様の)やり方で選択的な通信をサポートするには、通信行為(act of communicating)から通信の記述(description of a communication)を分離する必要がある。これをやるのが`Offer`だ。Offerは通信を記述する永続的な値であり、通信を(Offerに従って)実行するには、`sync()`メソッドによって同期化する。
  1125 +
  1126 + trait Offer[T] {
  1127 + def sync(): Future[T]
  1128 + }
  1129 +
  1130 +.LP `sync()`メソッドは、通信が行われた時にやり取りされた値を生成する<code>Future[T]</code>を返す。
  1131 +
  1132 +`Broker`は、Offerを介して値のやり取りを協調する。Brokerは通信のチャネルだ:
  1133 +
  1134 + trait Broker[T] {
  1135 + def send(msg: T): Offer[Unit]
  1136 + val recv: Offer[T]
  1137 + }
  1138 +
  1139 +.LP そして、二つのOfferを生成するとき、
  1140 +
  1141 + val b: Broker[Int]
  1142 + val sendOf = send(1)
  1143 + val recvOf = b.recv
  1144 +
  1145 +.LP <code>sendOf</code>と<code>recvOf</code>はどちらも同期化されており、
  1146 +
  1147 + // In process 1:
  1148 + sendOf.sync()
  1149 +
  1150 + // In process 2:
  1151 + recvOf.sync()
  1152 +
  1153 +.LP どちらのOfferも有効になり、<code>1</code>の値がやり取りされる。
  1154 +
  1155 +選択的な通信は、いくつかのOfferを`Offer.choose`で結合することにより実行される。
  1156 +
  1157 + def choose[T](ofs: Offer[T]*): Offer[T]
  1158 +
  1159 +.LP は、同期化すると<code>ofs</code>のうち、最初に利用可能になった唯一つのOfferを有効とする、新しいOfferを生成する。いくつかが即座に利用可能になった場合は、有効になる`Offer`はランダムに選ばれる。
  1160 +
  1161 +`Offer`オブジェクトは、一個限りのOfferをいくつも持っており、BrokerからのOfferを組み立てるために使用される。
  1162 +
  1163 + Offer.timeout(duration): Offer[Unit]
  1164 +
  1165 +.LP は、与えられた期間の後に起動するOfferだ。<code>Offer.never</code>は、決して有効にならない。また、<code>Offer.const(value)</code>は、与えられた値が直ちに有効になる。選択的な通信を用いて合成するのにも有用だ。例えば、送信操作でタイムアウトを適用するときは:
  1166 +
  1167 + Offer.choose(
  1168 + Offer.timeout(10.seconds),
  1169 + broker.send("my value")
  1170 + ).sync()
  1171 +
  1172 +OfferとBrokerを使う方法と、[SynchronousQueue](http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/SynchronousQueue.html)を比べてみたくなるかもしれないが、両者には微妙だが重要な違いがある。Offerは組み立てることができるが、SynchronousQueueのようなキューでは、とてもそんなことはできない。例えば、Brokerで表される一連のキューを考えると:
  1173 +
  1174 + val q0 = new Broker[Int]
  1175 + val q1 = new Broker[Int]
  1176 + val q2 = new Broker[Int]
  1177 +
  1178 +.LP ここで、読み込みのための結合されたキューを作ってみると:
  1179 +
  1180 + val anyq: Offer[Int] = Offer.choose(q0.recv, q1.recv, q2.recv)
  1181 +
  1182 +.LP <code>anyq</code>はOfferで、最初に利用可能になったキューから読み込む。ここで、<code>anyq</code>はやはり同期的であり、内部にあるキューの動作を利用できる。こうした合成は、キューを使う方法ではとても不可能だ。
  1183 +
  1184 +#### 例: 簡単なコネクションプール
  1185 +
  1186 +コネクションプールはネットワークアプリケーションでは一般的なもので、たいていは実装がとても難しい。例えば、個々のクライアントは異なるレイテンシを要求するため、多くの場合、プールからの取得にタイムアウトを持つことが望ましい。プールは、原理的には単純だ。コネクションのキューを保持し、待機クライアント(waiter)が入ってきたら要求を満たしてやる。従来の同期化プリミティブでは、典型的には二つのキューを保持する。一つはwaiterで、コネクション(connection)がない時に使われる。もう一つはconnectionで、これは待機クライアント(waiter)がない時に使われる。
  1187 +
  1188 +OfferとBrokerを使うと、これをとても自然に表現できる:
  1189 +
  1190 + class Pool(conns: Seq[Conn]) {
  1191 + private[this] val waiters = new Broker[Conn]
  1192 + private[this] val returnConn = new Broker[Conn]
  1193 +
  1194 + val get: Offer[Conn] = waiters.recv
  1195 + def put(c: Conn) { returnConn ! c }
  1196 +
  1197 + private[this] def loop(connq: Queue[Conn]) {
  1198 + Offer.choose(
  1199 + if (connq.isEmpty) Offer.never else {
  1200 + val (head, rest) = connq.dequeue
  1201 + waiters.send(head) { _ => loop(rest) }
  1202 + },
  1203 + returnConn.recv { c => loop(connq enqueue c) }
  1204 + ).sync()
  1205 + }
  1206 +
  1207 + loop(Queue.empty ++ conns)
  1208 + }
  1209 +
  1210 +`loop`は、返却されたコネクションを持つことを常にオファー(offer)し、キューが空でない時のみ送信をオファーする。永続的なキューを使うことで、推論をより単純にできる。プールに対するインタフェースもOfferを介しているから、もし呼び出し側がタイムアウトを適用したいなら、コンビネータを使うことで可能だ:
  1211 +
  1212 + val conn: Future[Option[Conn]] = Offer.choose(
  1213 + pool.get { conn => Some(conn) },
  1214 + Offer.timeout(1.second) { _ => None }
  1215 + ).sync()
  1216 +
  1217 +タイムアウトの実装に余計な簿記は必要とされない。これは、Offerの動作によるものだ: もし`Offer.timeout`が選択されたら、もはやプールからの受信をオファーしない。つまり、プールと呼び出し側がそれぞれ、Brokerである`waiters`上で送信と受信を同時に合意することはない。
  1218 +
  1219 +#### 例: エラトステネスの篩
  1220 +
  1221 +並行プログラムを、同期的に通信する一連のシーケンシャルなプロセスとして構築するのは、多くの場合で有用だし、時としてプログラムを非常に単純化できる。OfferとBrokerは、これを単純化しかつ統一化する手段を提供する。実際、それらのアプリケーションは、人が"古典的な"並行性の問題だとみなすかもしれないことを乗り越える。(OfferやBrokerを用いた)並行プログラミングは、サブルーチンやクラス、モジュールと同じように、有用な*構造化*ツールだ。これは、制約充足問題(Constraint Satisfaction Problem; CSP)からのもう一つの重要なアイデアだ。
  1222 +
  1223 +この一つの例は[エラトステネスの篩](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)で、整数ストリームに対するフィルタの連続的な適用として構造化できる。まず、整数の生成源が必要だ:
  1224 +
  1225 + def integers(from: Int): Offer[Int] = {
  1226 + val b = new Broker[Int]
  1227 + def gen(n: Int): Unit = b.send(n).sync() ensure gen(n + 1)
  1228 + gen(from)
  1229 + b.recv
  1230 + }
  1231 +
  1232 +.LP <code>integers(n)</code>は、<code>n</code>から始まる全ての連続した整数の単純なOfferだ。次に、フィルタが必要だ:
  1233 +
  1234 + def filter(in: Offer[Int], prime: Int): Offer[Int] = {
  1235 + val b = new Broker[Int]
  1236 + def loop() {
  1237 + in.sync() onSuccess { i =>
  1238 + if (i % prime != 0)
  1239 + b.send(i).sync() ensure loop()
  1240 + else
  1241 + loop()
  1242 + }
  1243 + }
  1244 + loop()
  1245 +
  1246 + b.recv
  1247 + }
  1248 +
  1249 +.LP <code>filter(in, p)</code>は、<code>in</code>から素数<code>p</code>の倍数を取り除くOfferを返す。最後に、篩(sieve)を定義する:
  1250 +
  1251 + def sieve = {
  1252 + val b = new Broker[Int]
  1253 + def loop(of: Offer[Int]) {
  1254 + for (prime <- of.sync(); _ <- b.send(prime).sync())
  1255 + loop(filter(of, prime))
  1256 + }
  1257 + loop(integers(2))
  1258 + b.recv
  1259 + }
  1260 +
  1261 +.LP <code>loop()</code>の動作は単純だ: <code>of</code>から次の素数を読み取った後、この素数を除外した<code>of</code>にフィルタを適用する。<code>loop</code>が再帰するにつれて連続した素数がフィルタされ、篩が手に入る。今や、我々は最初から10000個の素数を出力できる:
  1262 +
  1263 + val primes = sieve
  1264 + 0 until 10000 foreach { _ =>
  1265 + println(primes.sync()())
  1266 + }
  1267 +
  1268 +このアプローチは、単純かつ直行的なコンポーネントへと構造化できることに加えて、篩をストリームとして扱える。君は、興味がある素数の集合を演繹的に計算したり、さらにモジュラリティを拡張したりする必要がない。
1124 1269
1125 1270 ## 謝辞
1126 1271
1127 1272 本レッスンは、Twitter社のScalaコミュニティによるものだ。私は、誠実な記録者でありたい。
1128 1273
1129   -Blake MathenyとNick Kallen、そしてSteve Guryには、とても有益な助言と多くの優れた提案を与えてもらった。
  1274 +Blake MathenyとNick Kallen、Steve Gury、そしてRaghavendra Prabhuには、とても有益な助言と多くの優れた提案を与えてもらった。
1130 1275
1131 1276 ### 日本語版への謝辞
1132 1277

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.