Náhodné ako príklad scalaz.Monad - scala, random, monads, scalaz

Toto je nadviazanie na môj predchádzajúci otázka, Napísal som monad (na cvičenie), čo je vlastne funkcia generujúca náhodné hodnoty. Nie je však definovaný ako inštancie typovej triedy scalaz.Monad.

Teraz som sa pozrel na Rng knižnica a všimol si, že je definovaný Rng ako 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)
}

Zaujímalo by ma, ako presne z toho majú úžitok používatelia. Ako to môžeme využiť? Rng je príklad typovej triedy scalaz.Monad ? Môžete uviesť nejaké príklady?

odpovede:

3 pre odpoveď č. 1

Jednou z výhod je, že získate veľa užitočných metód definovaných v MonadOps.

Napríklad, Rng.double.iterateUntil(_ < 0.1) bude produkovať iba hodnoty, ktoré sú nižšie ako 0.1 (zatiaľ čo hodnoty väčšie ako 0.1 budú preskočené).

iterateUntil sa môže použiť na generovanie distribučných vzoriek pomocou metódy odmietnutia. Napr. toto je kód, ktorý vytvára generátor vzorky distribúcie verzie 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 pre odpoveď č. 2

Tu je jednoduchý príklad. Predpokladajme, že chcem vybrať náhodnú veľkosť rozsahu a potom vybrať náhodný index v tomto rozsahu a potom vrátiť rozsah aj index. Druhé výpočet náhodnej hodnoty jasne závisí od prvého - potrebujem poznať veľkosť rozsahu, aby som mohol vybrať hodnotu v rozsahu.

Toto je presne to, pre čo je monadická väzba - umožňuje vám napísať:

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

To by nebolo možné, keby sme nemali Monad inštancia pre Rng.


2 pre odpoveď č. 3

Ako každé rozhranie, deklarovanie inštancie Monad[Rng] robí dve veci: poskytuje implementáciu Monad metódy pod štandardnými názvami a vyjadruje implicitnú zmluvu, že tieto implementácie metód sú v súlade s určitými zákonmi (v tomto prípade monadovými zákonmi).

@Travis uviedol príklad jednej veci, ktorú implementujú tieto rozhrania, implementáciu Scalaz map a flatMap, Máte pravdu, že by ste ich mohli implementovať priamo; Monad (v skutočnosti o niečo zložitejšie).

Ako príklad metódy, na ktorú určite musíte implementovať nejaké rozhranie Scalaz, ako to funguje sequence? Toto je metóda, ktorá zmení a List (alebo všeobecnejšie a Traversable) súvislostí do jedného kontextu a List, napríklad:

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

Ale toto sa v skutočnosti používa iba Applicative[Rng] (čo je nadtrieda), nie plná sila Monad, Vlastne nemôžem myslieť na čokoľvek, čo používa Monad priamo (existuje niekoľko metód MonadOps, napr. untilM, ale nikdy som ich nepoužil v hneve), ale možno budete chcieť Bind pre prípad „obalu“, v ktorom máte „vnútorný“ Monad "vnútri" tvoja Rng veci, v takom prípade MonadTrans je užitočný:

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

Aby som bol úprimný, Applicative je pravdepodobne dosť dobrý pre väčšinu Monad prípady použitia, aspoň tie najjednoduchšie.

Samozrejme, všetky tieto metódy sú veci, ktoré ste vy mohol implementujte sa sami, ale rovnako ako všetky ostatné knižnice je aj v prípade Scalasu to, že sú už implementované a pod štandardnými názvami, čo ostatným ľuďom uľahčuje pochopenie vášho kódu.