Compreendendo a mônada aleatória no Scala

Este é um seguimento do meu anteriorPergunta, questão

Travis Brown apontou quejava.util.Random tem efeito colateral e sugeriu uma mônada aleatóriaRng biblioteca para tornar o código puramente funcional. Agora, estou tentando construir uma mônada aleatória simplificada sozinha para entender como ela funciona.

Isso faz sentido ? Como você corrige / aprimora a explicação abaixo?

Gerador Aleatório

Primeiro plagiamos uma função geradora aleatória dejava.util.Random

 // do some bit magic to generate a new random "seed" from the given "seed" 
 // and return both the new "seed" and a random value based on it

 def next(seed: Long, bits: Int): (Long, Int) = ...

Observe quenext retornaambos a nova semente e o valor, e não apenas o valor. Precisamos passar a nova semente para outra invocação de função.

Ponto aleatório

Agora vamos escrever uma função para gerar um ponto aleatório em um quadrado de unidade.

Suponha que tenhamos uma função para gerar um duplo aleatório no intervalo [0, 1]

 def randomDouble(seed: Long): (Long, Double) = ... // some bit magic

Agora podemos escrever uma função para gerar um ponto aleatório.

def randomPoint(seed: Long): (Long, (Double, Double)) = {
   val (seed1, x) = randomDouble(seed)
   val (seed2, y) = randomDouble(seed1)
   (seed2, (x, y)) 
}

Até agora, tudo bem e ambosrandomDouble erandomPoint são puros. O único problema é que compomosrandomDouble construirrandomPoint Ad hoc. Nós não temos umgenérico ferramenta para compor funções com valores aleatórios.

MônadaAleatória

Agora vamos definir umgenérico ferramenta para compor funções com valores aleatórios. Primeiro, generalizamos o tipo derandomDouble:

type Random[A] = Long => (Long, A) // generate a random value of type A

e, em seguida, crie uma classe de wrapper em torno dele.

class Random[A](run: Long => (Long, A))

Precisamos do wrapper para definir métodosflatMap (Comoligar em Haskell) emap usado porpara compreensão.

class Random[A](run: Long => (Long, A)) {
  def apply(seed: Long) = run(seed)  

  def flatMap[B](f: A => Random[B]): Random[B] =
    new Random({seed: Long => val (seed1, a) = run(seed); f(a)(seed1)})

  def map[B](f: A => B): Random[B] =
    new Random({seed: Long = val (seed1, a) = run(seed); (seed1, f(a))})
}  

Agora adicionamos umfábrica-função para criar uma trivialRandom[A] (que é absolutamente determinístico em vez de "aleatório"). Este é umRetorna funciona comoRetorna em Haskell).

def certain[A](a: A) = new Random({seed: Long => (seed, a)})

Random[A] é umcomputação produzindo valor aleatório do tipo A. Os métodosflatMap , mape funçãounit serve paracomposição cálculos simples para criar outros mais complexos. Por exemplo, vamos compor doisRandom[Double] construirRandom[(Double, Double)].

Ponto aleatório monádico

Agora, quando temos uma mônada, estamos prontos para revisitarrandomPoint erandomDouble. Agora, nós os definimos de maneira diferente como funções que produzemRandom[Double] eRandom[(Double, Double)]

def randomDouble(): Random[Double] = new Random({seed: Long => ... })
def randomPoint(): Random[(Double, Double)] =
  randomDouble().flatMap(x => randomDouble().flatMap(y => certain(x, y))

Esta implementação éMelhor do que o anterior, uma vez que utiliza umgenérico ferramenta (flatMap ecertain) para compor duas chamadas deRandom[Double] e construirRandom[(Double, Double)].

Agora pode reutilizar esta ferramenta para criar mais funções gerando valores aleatórios.

Cálculo de Pi de Monte Carlo

Agora podemos usarmap para testar se um ponto aleatório está no círculo:

def randomCircleTest(): Random[Boolean] = 
  randomPoint().map {case (x, y) => x * x + y * y <= 1} 

Também podemos definir uma simulação de Monte-Carlo em termos deRandom[A]

def monteCarlo(test: Random[Boolean], trials: Int): Random[Double] = ...

e, finalmente, a função para calcular PI

def pi(trials: Int): Random[Double] = ....

Todas essas funções são puras. Os efeitos colaterais ocorrem apenas quando finalmente aplicamos opi para obter o valor de pi.

questionAnswers(1)

yourAnswerToTheQuestion