(Nie znam Erlanga i nie mogę napisać do Haskella, ale myślę, że mimo to mogę odpowiedzieć)
W tym wywiadzie podany jest przykład biblioteki generowania liczb losowych. Oto możliwy stanowy interfejs:
# create a new RNG
var rng = RNG(seed)
# every time we call the next(ceil) method, we get a new random number
print rng.next(10)
print rng.next(10)
print rng.next(10)
Dane wyjściowe mogą być 5 2 7
. Dla kogoś, kto lubi niezmienność, jest to po prostu zły! Powinno tak być 5 5 5
, ponieważ wywołaliśmy metodę na tym samym obiekcie.
Czym byłby interfejs bezstanowy? Możemy zobaczyć sekwencję liczb losowych jako leniwie ocenianą listę, z której next
faktycznie pobiera się głowa:
let rng = RNG(seed)
let n : rng = rng in
print n
let n : rng = rng in
print n
let n : rng in
print n
Dzięki takiemu interfejsowi zawsze możemy przywrócić poprzedni stan. Jeśli dwa fragmenty twojego kodu odnoszą się do tego samego RNG, faktycznie otrzymają tę samą sekwencję liczb. W funkcjonalnym sposobie myślenia jest to bardzo pożądane.
Wdrożenie tego w stanowym języku nie jest takie skomplikowane. Na przykład:
import scala.util.Random
import scala.collection.immutable.LinearSeq
class StatelessRNG (private val statefulRNG: Random, bound: Int) extends LinearSeq[Int] {
private lazy val next = (statefulRNG.nextInt(bound), new StatelessRNG(statefulRNG, bound))
// the rest is just there to satisfy the LinearSeq trait
override def head = next._1
override def tail = next._2
override def isEmpty = false
override def apply(i: Int): Int = throw new UnsupportedOperationException()
override def length = throw new UnsupportedOperationException()
}
// print out three nums
val rng = new StatelessRNG(new Random(), 10)
rng.take(3) foreach (n => println(n))
Po dodaniu odrobiny cukru syntaktycznego, aby wyglądał jak lista, jest to całkiem niezłe.