像任何接口一样,声明 Monad[Rng]
的一个实例需要做两件事情:它提供了标准名称下的 Monad
方法的实现,并表达了这些方法实现符合特定法律(在这种情况下是单子定律)的隐式契约。
@Travis 给出了使用这些接口实现的一个示例,即 Scalaz 实现中的 map
和 flatMap
。你是对的,你可以直接实现它们;它们在 Monad
中被“继承”(实际上比这更加复杂)。
至于必须为某个 Scalaz 接口实现的方法的示例,那么 sequence
如何?这是一种将上下文的 List
(或更普遍地说,Traversable
)转换为 List
的单个上下文的方法,例如:
val randomlyGeneratedNumbers: List[Rng[Int]] = ...
randomlyGeneratedNumbers.sequence: Rng[List[Int]]
但实际上这仅仅使用了Applicative[Rng]
(它是一个超类),而没有充分发挥Monad
的全部威力。我实际上无法想到任何直接使用Monad
的内容(有一些在MonadOps
上的方法,比如untilM
,但我从未愤怒地使用过它们中的任何一个),但您可能需要一个Bind
作为一个“包装器”的情况,其中您拥有一个“内部”Monad
“在”您的Rng
事物中,此时MonadTrans
非常有用:
val a: Rng[Reader[Config, Int]] = ...
def f: Int => Rng[Reader[Config, Float]] = ...
val b: ReaderT[Rng, Config, Int] = ...
val g: Int => ReaderT[Rng, Config, Float] = ...
b >>= g
老实说,Applicative
对于大多数 Monad
使用场景来说已经足够好了,至少对于简单的情况是这样。
当然,所有这些方法你都可以自己实现,但就像任何库一样,Scalaz 的整个重点在于它们已经被实现了,并且使用标准名称,这使得其他人更容易理解你的代码。
Rng
没有定义为scalaz.Monad
的实例,这个例子也可以工作。只需将flatMap
和map
添加到Rng
中(如果我们添加一个return
函数并检查法律,则会使其成为单子),是吗?我特别询问那个隐式的val RngMonad:Monad [Rng] = ...
东西。 - Michaelscalaz.Monad
实例就是“将某物变成单子”的所涉及的内容。通过MonadOps
获得其他一些方法似乎有点偶然性。 - Travis BrownRng
设为一个单子有什么好处?目前我只看到一个好处,那就是与scalaz.Monad
一起工作时可以使用 Scalaz 的方法。也许我漏掉了什么…… - MichaelMonadOps
中的操作)可以在高层次上定义并在许多上下文中使用。为什么要为Rng
编写一个特殊的whileM
,而不是编写一个适用于任何单子的函数呢? - Travis Brownscalaz.Monad
中并可应用于“随机生成器”领域的通用操作。例如,可以应用iterateUntil
来编写Beta样本生成器(如下所示)。我会考虑使用whileM
的情况。 - Michael