Don Stewart が正しく指摘したように、状態モナドではなく ST モナドを探しているようです。だから私の答えはそれについてです。
ST モナドは、Haskell でローカルな可変性を許可するために使用されますが、通常は可変性が許可されていません。たとえば、命令型sum
関数を書きたい場合は、ST モナドを使用します。scalaz でのそのような関数の例は次のようになります (ここから取得):
def sumST[S, A](as: List[A])(implicit A: Numeric[A]): ST[S, A] =
for { n <- newVar(A.zero)
_ <- as.traverseU(a => n.mod(A.plus(_, a)))
m <- n.read } yield m
def sum[A : Numeric](as: List[A]): A =
runST(new Forall[({type λ[S] = ST[S, A]})#λ] {
def apply[S] = sumST[S, A](as)
})
明らかに、scala では次のように書くこともできます。
def sum[A](xs: List[A])(implicit N: Numeric[A]) = {
var sum = N.zero
val it = xs.iterator
while (it.hasNext) {
sum = N.plus(sum, it.next)
}
sum
}
変更可能な状態は関数のスコープをエスケープしないため、これは依然として参照透過的です。Haskell では、単に . を持つことができないため、これらの場合に使用されますvar
。
では、なぜ scala で ST を使用する必要があるのでしょうか? 変更可能な構造 (配列など) で作業したい場合、この変更可能な構造が計算のスコープから逃れることはなく、最初に不変の構造に変換する必要があることを保証したい場合は、ST を使用できます。scalaz にはfor があり、ST モナドが実行STArray
されているときに に変わります。ImmutableArray
これの良い例は scalaz のバイナリソートの例です。Rúnar Bjarnasonの ST モナドに関するブログ記事も読む価値があります。