如何使用scalaz.WriterT登录for表达式?
如何使用scalaz.WriterT进行日志记录?关于monad transformers 这是一个非常简短的介绍。您可能会找到关于或此的更多信息 单子不合成,这意味着如果你有一个单子如何使用scalaz.WriterT登录for表达式?,scala,logging,scalaz,Scala,Logging,Scalaz,如何使用scalaz.WriterT进行日志记录?关于monad transformers 这是一个非常简短的介绍。您可能会找到关于或此的更多信息 单子不合成,这意味着如果你有一个单子a[\u]和一个单子B[\u],那么a[B[\u]]就不能自动派生。然而,在大多数情况下,这可以通过为给定的monad使用所谓的monad转换器来实现 如果我们有monad transformerBT用于monadB,那么我们可以为任何monada组合一个新的monada[B[\u]]。没错,通过使用BT,我们可以
a[\u]
和一个单子B[\u]
,那么a[B[\u]]
就不能自动派生。然而,在大多数情况下,这可以通过为给定的monad使用所谓的monad转换器来实现
如果我们有monad transformerBT
用于monadB
,那么我们可以为任何monada
组合一个新的monada[B[\u]]
。没错,通过使用BT
,我们可以把B
放在A
里面
scalaz中的Monad变压器使用情况
下面假设Scalaz7,因为坦率地说,我没有在Scalaz6中使用monad变压器
monad transformerMT
接受两个类型参数,第一个是包装器(外部)monad,第二个是monad堆栈底部的实际数据类型。注意:可能需要更多的类型参数,但这些参数与转换器无关,而是特定于给定的monad(如写入程序的记录类型,或验证的错误类型)
因此,如果我们有一个List[Option[a]]
,我们想把它当作一个单一的合成单子,那么我们需要Option[List,a]
。如果我们有选项[List[A]]
,我们需要ListT[Option,A]
怎么去那里?如果我们有非变压器值,我们通常可以用MT将其包装。apply
获取变压器内部的值。为了从转换后的表单恢复到正常状态,我们通常对转换后的值调用。run
因此val a:OptionT[List,Int]=OptionT[List,Int](List(一些(1))
和val b:List[Option[Int]=a.run
是相同的数据,只是表示方式不同
托尼·莫里斯(Tony Morris)建议,最好尽早进入转换版本,并尽可能长时间使用
注意:使用transformers组合多个Monad会产生一个类型与正常数据类型顺序相反的transformer堆栈。因此,正常的List[Option[Validation[E,a]]
看起来类似于type ListOptionValidation[+E,+a]=ValidationT[({type l[+a]=OptionT List,a]}\l,E,a]
更新:自scalaz 7.0.0-M2起,Validation
不是(正确的)单子,因此ValidationT
不存在。请改用EitherT
使用WriterT进行日志记录
import scalaz.{Writer}
import scalaz.std.list.listMonoid
import scalaz._
def calc1 = Writer(List("doing calc"), 11)
def calc2 = Writer(List("doing other"), 22)
val r = for {
a <- calc1
b <- calc2
} yield {
a + b
}
r.run should be_== (List("doing calc", "doing other"), 33)
根据您的需要,您可以使用WriterT
,而不使用任何特定的外部monad(在本例中,在后台它将使用Id
monad,它不做任何事情),或者可以将日志放在monad中,或者将monad放在日志中
第一种情况,简单日志记录
import scalaz.{Writer}
import scalaz.std.list.listMonoid
import scalaz._
def calc1 = Writer(List("doing calc"), 11)
def calc2 = Writer(List("doing other"), 22)
val r = for {
a <- calc1
b <- calc2
} yield {
a + b
}
r.run should be_== (List("doing calc", "doing other"), 33)
使用这种方法,因为日志记录在选项
monad中,如果任何绑定选项是None
,我们只会得到一个None
结果,而不需要任何日志
注意:x.point[Option]
与Some(x)
在效果上相同,但可能有助于更好地概括代码。并非致命性,只是暂时这样做
第三个选项,在monad之外登录
import scalaz.{Writer, OptionT}
import scalaz.std.list.listMonoid
import scalaz.std.option.optionInstance
import scalaz.syntax.pointed._
type Logger[+A] = WriterT[scalaz.Id.Id, List[String], A]
def calc1 = OptionT[Logger, Int](Writer(List("doing calc"), Some(11): Option[Int]))
def calc2 = OptionT[Logger, Int](Writer(List("doing other"), None: Option[Int]))
val r = for {
a <- calc1
b <- calc2
} yield {
a + b
}
r.run.run should be_== (List("doing calc", "doing other") -> None)
导入scalaz.{Writer,OptionT}
导入scalaz.std.list.listMonoid
导入scalaz.std.option.optionInstance
导入scalaz.syntax.pointed_
类型记录器[+A]=WriterT[scalaz.Id.Id,List[String],A]
def calc1=OptionT[Logger,Int](编写器(列表(“正在计算”),一些(11):选项[Int]))
def calc2=OptionT[Logger,Int](编写器(列表(“执行其他”),无:选项[Int]))
val r=用于{
atype OptionLogger[a]=WriterT[Option,NonEmptyList[String],a]
valtwo:OptionLogger[Int]=WriterT.put(2.some)(“数字2.pure[NonEmptyList])
val一百:OptionLogger[Int]=WriterT.put(100.some)(“一百”.pure[NonEmptyList])
瓦尔两百={
注意:scalaz seven head即将推出一个MonadWriter
typeclass。值得关注。我记得我把它看作是要点(由@puffnfresh发布)
type OptionLogger[A] = WriterT[Option, NonEmptyList[String], A]
val two: OptionLogger[Int] = WriterT.put(2.some)("The number two".pure[NonEmptyList])
val hundred: OptionLogger[Int] = WriterT.put(100.some)("One hundred".pure[NonEmptyList])
val twoHundred = for {
a <- two
b <- hundred
} yield a * b
twoHundred.value must be equalTo(200.some)
val log = twoHundred.written map { _.list } getOrElse List() mkString(" ")
log must be equalTo("The number two One hundred")