8

This is a follow-up to my previous question. I wrote a monad (for an exercise) that is actually a function generating random values. However it is not defined as an instance of type class scalaz.Monad.

Now I looked at Rng library and noticed that it defined Rng as 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)
   }

So I wonder how exactly users benefit from that. How can we use the fact that Rng is an instance of type class scalaz.Monad ? Can you give any examples ?

Community
  • 1
  • 1
Michael
  • 41,026
  • 70
  • 193
  • 341

3 Answers3

7

Here's a simple example. Suppose I want to pick a random size for a range, and then pick a random index inside that range, and then return both the range and the index. The second computation of a random value clearly depends on the first—I need to know the size of the range in order to pick a value in the range.

This kind of thing is specifically what monadic binding is for—it allows you to write the following:

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

This wouldn't be possible if we didn't have a Monad instance for Rng.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 1
    Thank you. I think I got it but I am asking about smth. else. This example works even if `Rng` is not defined an instance of `scalaz.Monad` technically. It's enough just to add `flatMap` and `map` to `Rng` (that makes it a monad if we add a `return` function and check the laws), isn't it ? I am asking specifically about that implicit `val RngMonad: Monad[Rng] = ...` thing. – Michael Nov 24 '14 at 11:04
  • Right, the syntactic sugar would work anyway, but when you're working in the context of Scalaz, providing a `scalaz.Monad` instance is what's involved in "making something a monad". The fact that you get some other methods via `MonadOps` seems kind of incidental. – Travis Brown Nov 24 '14 at 14:14
  • I see. So I probably should rephrase the question: what are the benefits of making `Rng` a monad _in the context_ of Scalaz ? The only benefit I see now is Scalaz methods that work with/against `scalaz.Monad`. Maybe I am missing something ... – Michael Nov 24 '14 at 15:02
  • Well, generalization and reuse are a couple of reasons. Lots of useful operations (including the ones in `MonadOps`) can be defined at a high level and used in many contexts. Why write e.g. a special `whileM` for `Rng` when you can write one that works for any monad? – Travis Brown Nov 24 '14 at 15:24
  • Generalization and reuse are what I am looking for. More precisely, I am looking for such _generic_ operations defined for `scalaz.Monad` that can be applied in the "random generators" domain. For example, `iterateUntil` can be applied to write a beta sample generator (as it is shown below). I will think about a use case for `whileM`. – Michael Nov 24 '14 at 15:54
3

One of the benefit is that you will get a lot of useful methods defined in MonadOps.

For example, Rng.double.iterateUntil(_ < 0.1) will produce only the values that are less than 0.1 (while the values greater than 0.1 will be skipped).

iterateUntil can be used for generation of distribution samples using a rejection method. E.g. this is the code that creates a beta distribution sample generator:

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
}
ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
  • Thank you. This is the kind of examples I am looking for. Unfortunately, I don't what "beta generator" is. I would prefer a simpler example of `iterateUntil`. – Michael Nov 24 '14 at 11:31
  • BTW, can I use `iterateUntil` to generate a sequence of random values of a given size ? – Michael Nov 24 '14 at 11:32
  • To generate a sequence of random values use can use `Rng.fill` – ZhekaKozlov Nov 24 '14 at 11:52
  • I am more interested in the implementation. `Rng.fill` is implemented w/ `sequence`. I wonder if it can be implemented with `iterateUntil` as well. – Michael Nov 24 '14 at 12:56
  • 1
    No, I don't think so. But you can use `rng.replicateM` to get the same result as `Rng.fill`. – ZhekaKozlov Nov 24 '14 at 13:19
2

Like any interface, declaring an instance of Monad[Rng] does two things: it provides an implementation of the Monad methods under standard names, and it expresses an implicit contract that those method implementations conform to certain laws (in this case, the monad laws).

@Travis gave an example of one thing that's implemented with these interfaces, the Scalaz implementation of map and flatMap. You're right that you could implement these directly; they're "inherited" in Monad (actually a little more complex than that).

For an example of a method that you definitely have to implement some Scalaz interface for, how about sequence? This is a method that turns a List (or more generally a Traversable) of contexts into a single context for a List, e.g.:

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

But this actually only uses Applicative[Rng] (which is a superclass), not the full power of Monad. I can't actually think of anything that uses Monad directly (there are a few methods on MonadOps, e.g. untilM, but I've never used any of them in anger), but you might want a Bind for a "wrapper" case where you have an "inner" Monad "inside" your Rng things, in which case MonadTrans is useful:

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

To be totally honest though, Applicative is probably good enough for most Monad use cases, at least the simpler ones.

Of course all of these methods are things you could implement yourself, but like any library the whole point of Scalaz is that they're already implemented, and under standard names, making it easier for other people to understand your code.

lmm
  • 17,386
  • 3
  • 26
  • 37
  • Thank you. The `sequence` is the example I am thinking of. It is Ok that it uses `Applicative`. – Michael Nov 24 '14 at 11:35