设为首页 加入收藏

TOP

Scalaz(13)- Monad:Writer - some kind of logger(一)
2017-10-10 12:13:31 】 浏览:6329
Tags:Scalaz Monad Writer some kind logger

  通过前面的几篇讨论我们了解到F[T]就是FP中运算的表达形式(representation of computation)。在这里F[]不仅仅是一种高阶类型,它还代表了一种运算协议(computation protocol)或者称为运算模型好点,如IO[T],Option[T]。运算模型规范了运算值T的运算方式。而Monad是一种特殊的FP运算模型M[A],它是一种持续运算模式。通过flatMap作为链条把前后两个运算连接起来。多个flatMap同时作用可以形成一个程序运行链。我们可以在flatMap函数实现中增加一些附加作用,如维护状态值(state value)、跟踪记录(log)等。 

  在上一篇讨论中我们用一个Logger的实现例子示范了如何在flatMap函数实现过程中增加附加作用;一个跟踪功能(logging),我们在F[T]运算结构中增加了一个String类型值作为跟踪记录(log)。在本篇讨论中我们首先会对上篇的Logger例子进行一些log类型的概括,设计一个新的Logger结构:

1 case class Logger[LOG, A](log: LOG, value: A) { 2     def map[B](f: A => B): Logger[LOG,B] = Logger(log, f(value)) 3     def flatMap[B](f: A => Logger[LOG,B])(implicit M: Monoid[LOG]): Logger[LOG,B] = { 4         val nxLogger = f(value) 5         Logger(log |+| nxLogger.log, nxLogger.value) 6  } 7       
8 }

以上Logger对LOG类型进行了概括:任何拥有Monoid实例的类型都可以,能够支持Monoid |+|操作符号。这点从flatMap函数的实现可以证实。

当然我们必须获取Logger的Monad实例才能使用for-comprehension。不过由于Logger有两个类型参数Logger[LOG,A],我们必须用type lambda把LOG类型固定下来,让Monad运算只针对A类型值:

1 object Logger { 2     implicit def toLogger[LOG](implicit M: Monoid[LOG]) = new Monad[({type L[x] = Logger[LOG,x]})#L] { 3         def point[A](a: => A) = Logger(M.zero,a) 4         def bind[A,B](la: Logger[LOG,A])(f: A => Logger[LOG,B]): Logger[LOG,B] = la flatMap f 5  } 6 }

有了Monad实例我们可以使用for-comprehension:

 1 def enterInt(x: Int): Logger[String, Int] = Logger("Entered Int:"+x, x)  2                                                   //> enterInt: (x: Int)Exercises.logger.Logger[String,Int]
 3 def enterStr(x: String): Logger[String, String] = Logger("Entered String:"+x, x)  4                                                   //> enterStr: (x: String)Exercises.logger.Logger[String,String]
 5 
 6 for {  7     a <- enterInt(3)  8     b <- enterInt(4)  9     c <- enterStr("Result:") 10 } yield c + (a * b).shows                         //> res0: Exercises.logger.Logger[String,String] = Logger(Entered Int:3Entered I 11                                                   //| nt:4Entered String:Result:,Result:12)

不过必须对每个类型定义操作函数,用起来挺不方便的。我们可以为任何类型注入操作方法:

1 final class LoggerOps[A](a: A) { 2     def applyLog[LOG](log: LOG): Logger[LOG,A] = Logger(log,a) 3 } 4 implicit def toLoggerOps[A](a: A) = new LoggerOps[A](a) 5                                                   //> toLoggerOps: [A](a: A)Exercises.logger.LoggerOps[A]

我们为任意类型A注入了applyLog方法:

1 3.applyLog("Int three")                           //> res1: Exercises.logger.Logger[String,Int] = Logger(Int three,3)
2 "hi" applyLog "say hi"                            //> res2: Exercises.logger.Logger[String,String] = Logger(say hi,hi)
3 for { 4     a <- 3 applyLog "Entered Int 3"
5     b <- 4 applyLog "Entered Int 4"
6     c <- "Result:" applyLog "Entered String 'Result'"
7 } yield c + (a * b).shows                         //> res3: Exercises.logger.Logger[String,String] = Logger(Entered Int 3Entered 8                                                   //| Int 4Entered String 'Result',Result:12)

用aplyLog这样操作方便多了。由于LOG可以是任何拥有Monoid实例的类型。除了String类型之外,我们还可以用Vector或List这样的高阶类型:

1 for { 2     a <- 3 applyLog Vector("Entered Int 3") 3     b <- 4 applyLog Vector("Entered Int 4") 4     c <- "Result:" applyLog Vector("Entered String 'Result'") 5 } yield c + (a * b).shows                         //> res4: Exercises.logger.Logger[scala.collection.immutable.Vector[String],Str 6                                                   //| ing] = Logger(Vector(Entered Int 3, Entered Int 4, Entered String 'Result') 7                                                   //| ,Result:12)

一般来讲,用Vector效率更高,在下面我们会证实这点。

既然A可以是任何类型,那么高阶类型如Option[T]又怎样呢:

1 for {
2     oa <- 3.some applyLog Vector("Entered So
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(12)- Monad:再述述fla.. 下一篇Scalaz(14)- Monad:函数组合..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目