Понимание случайной монады в Scala

Это продолжение моего предыдущеговопрос

Трэвис Браун указал, чтоjava.util.Random побочный эффект и предложил случайную монадуRng библиотека сделать код чисто функциональным. Теперь я пытаюсь создать упрощенную случайную монаду, чтобы понять, как она работает.

Имеет ли это смысл ? Как бы вы исправить / улучшить объяснение ниже?

Генератор случайных чисел

Сначала мы плагиат случайной производящей функции изjava.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) = ...

Обратите внимание, чтоnext возвращаетсяи то и другое новое семя и ценность, а не просто ценность. Нам нужно, чтобы передать новое начальное значение другому вызову функции.

Случайная точка

Теперь давайте напишем функцию для генерации случайной точки в единичном квадрате.

Предположим, у нас есть функция для генерации случайного двойного в диапазоне [0, 1]

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

Теперь мы можем написать функцию для генерации случайной точки.

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

Пока все хорошо и обаrandomDouble а такжеrandomPoint чисты Единственная проблема в том, что мы сочиняемrandomDouble строитьrandomPoint для этого случая, У нас нетобщий инструмент для составления функций, дающих случайные значения.

монадаслучайный

Теперь мы определимобщий инструмент для составления функций, дающих случайные значения. Во-первых, мы обобщаем типrandomDouble:

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

а затем создайте класс-оболочку вокруг него.

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

Нам нужна оболочка для определения методовflatMap (какпривязывать в Хаскеле) иmap использовандля понимания,.

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))})
}  

Теперь мы добавляемзавод-функция для создания тривиальногоRandom[A] (что, кстати, абсолютно детерминировано, а не «случайно»)вернуть функция (каквернуть в Хаскеле).

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

Random[A] этовычисление дает случайное значение типа А. МетодыflatMap , mapи функцияunit служит длясоставление простые вычисления, чтобы построить более сложные. Например, мы составим дваRandom[Double] строитьRandom[(Double, Double)].

Монадическая случайная точка

Теперь, когда у нас есть монада, мы готовы вернуться кrandomPoint а такжеrandomDouble, Теперь мы определяем их по-разному как функции, дающиеRandom[Double] а такжеRandom[(Double, Double)]

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

Эта реализациялучше чем предыдущий, так как он используетобщий инструмент (flatMap а такжеcertain) составить два вызоваRandom[Double] и построитьRandom[(Double, Double)].

Теперь можно повторно использовать этот инструмент для создания большего количества функций, генерирующих случайные значения.

Монте-Карло расчет Пи

Теперь мы можем использоватьmap проверить, находится ли случайная точка в круге:

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

Мы также можем определить симуляцию Монте-Карло в терминахRandom[A]

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

и, наконец, функция для расчета PI

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

Все эти функции чисты. Побочные эффекты возникают только тогда, когда мы наконец применяемpi функция, чтобы получить значение пи.

Ответы на вопрос(1)

Ваш ответ на вопрос