/ / Losowo jako przykład scalaz.Monad - scala, random, monads, scalaz

Losowo jako przykład scalaz.Monad - scala, random, monads, scalaz

To kontynuacja mojego poprzedniego pytanie. Napisałem monadę (dla ćwiczenia), która w rzeczywistości jest funkcją generującą losowe wartości. Jednak nie jest zdefiniowane jako instancja klasy typu scalaz.Monad.

Teraz spojrzałem na Rng biblioteka i zauważyłem, że to zdefiniowało Rng tak jak scalaz.Monad:

implicit val RngMonad: Monad[Rng] =
new Monad[Rng] {
def bind[A, B](a: Rng[A])(f: A => Rng[B]) = a flatMap f
def point[A](a: => A) = insert(a)
}

Zastanawiam się więc, jak dokładnie użytkownicy z tego korzystają. Jak możemy wykorzystać fakt, że Rng jest instancją klasy typu scalaz.Monad ? Czy możesz podać jakieś przykłady?

Odpowiedzi:

3 dla odpowiedzi № 1

Jedną z korzyści jest to, że otrzymasz wiele przydatnych metod zdefiniowanych w MonadOps.

Na przykład, Rng.double.iterateUntil(_ < 0.1) da tylko wartości mniejsze niż 0.1 (podczas gdy wartości większe niż 0.1 zostanie pominięty).

iterateUntil może być wykorzystywany do generowania próbek dystrybucji przy użyciu metody odrzucania. Na przykład. to jest kod, który tworzy przykładowy generator dystrybucji beta:

import com.nicta.rng.Rng
import java.lang.Math
import scalaz.syntax.monad._

object Main extends App {

def beta(alpha: Double, beta: Double): Rng[Double] = {
// Purely functional port of Numpy"s beta generator: https://github.com/numpy/numpy/blob/31b94e85a99db998bd6156d2b800386973fef3e1/numpy/random/mtrand/distributions.c#L187
if (alpha <= 1.0 && beta <= 1.0) {
val rng: Rng[Double] = Rng.double

val xy: Rng[(Double, Double)] = for {
u <- rng
v <- rng
} yield (Math.pow(u, 1 / alpha), Math.pow(v, 1 / beta))

xy.iterateUntil { case (x, y) => x + y <= 1.0 }.map { case (x, y) => x / (x + y) }
} else ???
}

val rng: Rng[List[Double]] = beta(0.5, 0.5).fill(10)

println(rng.run.unsafePerformIO) // Prints 10 samples of the beta distribution
}

7 dla odpowiedzi nr 2

Oto prosty przykład. Załóżmy, że chcę wybrać losowy rozmiar dla zakresu, a następnie wybrać losowy indeks w tym zakresie, a następnie zwrócić zarówno zakres, jak i indeks. Drugie obliczenie wartości losowej wyraźnie zależy od pierwszego - muszę znać rozmiar zakresu, aby wybrać wartość z tego zakresu.

Właśnie tego służy wiązanie monadyczne - pozwala napisać następujące rzeczy:

val rangeAndIndex: Rng[(Range, Int)] = for {
max <- Rng.positiveint
index <- Rng.chooseint(0, max)
} yield (0 to max, index)

Nie byłoby to możliwe, gdybyśmy nie mieli Monad instancja dla Rng.


2 dla odpowiedzi nr 3

Jak każdy interfejs, deklarując wystąpienie Monad[Rng] robi dwie rzeczy: zapewnia implementację Monad metody pod standardowymi nazwami i wyraża niejawną umowę, że te implementacje metod są zgodne z niektórymi przepisami (w tym przypadku przepisami dotyczącymi monady).

@ Travis podał przykład jednej rzeczy zaimplementowanej z tymi interfejsami, implementacji Scalaz map i flatMap. Masz rację, że możesz je wdrożyć bezpośrednio, są one „odziedziczone” w Monad (właściwie trochę bardziej skomplikowany).

Na przykład metoda, dla której zdecydowanie musisz zaimplementować interfejs Scalaz sequence? Jest to metoda, która zamienia List (lub bardziej ogólnie a Traversable) kontekstów w jednym kontekście dla Listnp .:

val randomlyGeneratedNumbers: List[Rng[Int]] = ...
randomlyGeneratedNumbers.sequence: Rng[List[Int]]

Ale tak naprawdę używa tylko Applicative[Rng] (która jest nadklasą), a nie pełną mocą Monad. Naprawdę nie mogę wymyślić niczego, co by używało Monad bezpośrednio (istnieje kilka metod na MonadOps, np. untilM, ale nigdy nie użyłem żadnego z nich w gniewie), ale możesz chcieć Bind w przypadku „opakowania”, w którym masz „wewnętrzny” Monad „wewnątrz” twojego Rng rzeczy, w którym to przypadku MonadTrans jest użyteczny:

val a: Rng[Reader[Config, Int]] = ...
def f: Int => Rng[Reader[Config, Float]] = ...
//would be a pain to manually implement something to combine a and f
val b: ReaderT[Rng, Config, Int] = ...
val g: Int => ReaderT[Rng, Config, Float] = ...
b >>= g

Szczerze mówiąc, Applicative jest prawdopodobnie wystarczający dla większości Monad przypadki użycia, przynajmniej te najprostsze.

Oczywiście wszystkie te metody są dla ciebie mógłby zaimplementuj siebie, ale jak każda biblioteka, cały Scalaz polega na tym, że są one już zaimplementowane i mają standardowe nazwy, co ułatwia innym osobom zrozumienie twojego kodu.