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.
iterateUntil
. – Tragopan