他の人が指摘したように、あなたが質問を組み立てた方法は、それに答えるのを少し難しくしています。おそらくそれは、あなたがアクターに焦点を当てているためであり、あなたが作成した例は少し奇妙です.
要するに、Scala とその Actor 実装、および Actor の Akka 実装を使用すると、可変性で頭を撃ち抜くことができます。それがあなたが求めている中心的な質問のようです。それでよければ、ここで読むのをやめてください :)
ただし、不変性は並行プログラムの品質をさまざまな方法で向上させます。Scala の可変性と不変性を OO および関数型プログラミングと組み合わせることで、物事を実装する方法に多くの柔軟性がもたらされます。
人々が言わないように見えることは、プログラミングの概念には、ほとんどの命令型プログラマーが慣れているものとは異なるスタイルのプログラミングが不変に必要であるということです(私が学び始めたとき、私は確かにそれに慣れていませんでした)。多くの人は、これまでと同じ方法でコーディングし、可変オブジェクトを不変オブジェクトに置き換えて、利益を得ることができると考えているようです。そうではありません。
例を挙げると (一見どこにList
でもある例ですが)、それを使用するコードをどのように書くかがわかります。
List
Akka の Future 実装を使用して、Int の s で本当にばかげたことを実行します。アルゴリズムはそれほど重要ではありません。重要なのは、そこにないもの、つまり同時実行保護を認識することです。完全に不変であるため、同時アクセスの問題について考える必要さえありません。何が起こっているのかを明確にするために、以下に型注釈を追加しました - Scala は実際にすべてを推測します。
import akka.dispatch.{ExecutionContext, Future, Await}
import akka.util.duration._
import java.util.concurrent.Executors
object Main {
val execService = Executors.newCachedThreadPool()
implicit val execContext = ExecutionContext.fromExecutorService(execService)
def main(args: Array[String]) {
// A Future list of 1's
val flist: Future[List[Int]] = Future { (1 to 5) map { _: Int => 1 } toList }
// The goal is to create a new list of Ints in 25 "iterations" across
// multiple threads
val result: Future[List[Int]] = (1 to 5).foldLeft(flist) { (acc, _) =>
// "Loop" 5 times, creating 5 new lists of Ints, but these 'new' lists
// share most of their content with the previous list
val fs: IndexedSeq[Future[List[Int]]] = (1 to 5) map { i =>
acc map { numlist =>
(i * numlist.sum) :: numlist
}
}
// Reduce the 5 lists we just created back down to one list again, by
// performing a pairwise sum across them all... in the Future
Future.reduce(fs) { (a, l) =>
(a zip l) map { case (i, j) => i + j }
}
}
// Wait for the concurrency to complete and print out the final list
println(Await.result(result, 1 second))
// Shut down the execution system that was running our stuff concurrently
execContext.shutdown()
}
}
// Prints: List(12000000, 3000000, 750000, 187500, 46875, 3125, 3125, 3125, 3125, 3125)
(i * numlist.sum) :: numlist
からのすべての使用ビットで作成される中間リストnumlist
。そのループから出てくる 5 つの新しいリストは、実際にはnumlist
、5 つのまったく新しいリストではなく、へのポインタを持つ 5 つの新しい値として作成されます。つまりlist1 = "newvalue" + oldlist
、list2 = "another new value" + oldlist
など...
プログラミングは、変換のモデルを不変に意味します。データ構造を取得し、それを何かに渡し、それを変換して他の人に渡します。変更可能なプログラミングの方がはるかに高速で信頼性が高く、実行内容によっては遅くなる可能性もありますが、@mhs が述べたように、結論としては、安全性の観点から推論する方が簡単です。
これが命令型プログラミングの通常のモードにどのようにマップされるかは明らかではありません。一般にそうではないからです。また、それがすぐに意味をなさない理由でもあります。コーディングは他のことと同じで、新しいパラダイムを真に理解するには時間がかかります。