(Je ne connais pas Erlang, et je ne peux pas écrire Haskell, mais je pense que je peux quand même répondre)
Eh bien, dans cette interview, l'exemple d'une bibliothèque de génération de nombres aléatoires est donné. Voici une interface avec état possible:
# 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)
La sortie peut être 5 2 7
. Pour quelqu'un qui aime l'immuabilité, c'est tout simplement faux! Cela devrait l'être 5 5 5
, car nous avons appelé la méthode sur le même objet.
Alors, quelle serait une interface sans état? Nous pouvons voir la séquence de nombres aléatoires comme une liste évaluée paresseusement, où next
récupère en fait la tête:
let rng = RNG(seed)
let n : rng = rng in
print n
let n : rng = rng in
print n
let n : rng in
print n
Avec une telle interface, nous pouvons toujours revenir à un état précédent. Si deux morceaux de votre code font référence au même RNG, ils obtiendront en fait la même séquence de chiffres. Dans un état d'esprit fonctionnel, cela est hautement souhaitable.
L'implémenter dans un langage avec état n'est pas si compliqué. Par exemple:
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))
Une fois que vous avez ajouté un peu de sucre syntaxique pour que cela ressemble à une liste, c'est en fait assez agréable.