首页 > 代码库 > 12.7.1 创建日志记录的计算

12.7.1 创建日志记录的计算

12.7.1 创建日志记录的计算

 

这个计算将产生一个值,并能够将消息写入到本地日志记录缓冲区。这样,计算的结果将成为一个值,和包含消息的字符串列表。同样,我们使用只有一个识别器的差别联合,表示这个类型:

 

type Logging<‘T> = 

  | Log of ‘T * list<string>

 

这个类型非常类似于我们先前讨论的 ValueWrapper <‘a> 示例,只是加上了一个 F# 列表,表示写到日志的消息。现在,我们已有了类型,就可以实现计算生成器了。通常,我们需要实现 Bind 和 Return 成员;此外,还会实现一个新成员Zero,能够写不返回任何值的计算。后面,我们将会看到它是如何使用的。

清单 12.23 是这个生成器的实现。最重要的是Bind 成员,它把原始值的日志消息,和由计算的其余部分(是给 Bind 成员参数的函数)生成的值,连接起来。

 

清单 12.23 增加了日志功能的计算生成器 (F#)

type LoggingBuilder() = 

  member x.Bind(Log(value, logs1), f)=     [1]

    let (Log(newValue,logs2)) = f(value)      [2]

    Log(newValue, logs1 @logs2)     [3]

  member x.Return(value) = 

    Log(value, [])     <-- 增加了空日志值

  member x.Zero() = 

    Log((), [])     <-- 不返回空日志值

 

let log = new LoggingBuilder()

 

与其他例子一样,最困难的部分是实现 Bind 成员。我们的日志记录类型遵循所有正常的步骤,包括option 和 ValueWrapper 类型,和省略的第三步:

    1、我们要提取值。因为使用只有一种情况的差别联合,在成员的参数列表中[1],可以使用模式匹配;

    2、如果有值需要计算,就调用计算的其余部分。在清单 12.23 中,总是有值,因此,可以运行给定的函数[2]。我们没有立即返回结果,而是先分解,得到在执行过程中产生的新的值和日志消息。

    3、我们已经收集了两个日志消息的缓冲,所以,需要打包这个新的值,并为它添加新的日志状态。若要创建新状态,我们把原始的消息列表,和在调用计算的其余部分时生成的新列表连接起来[3]。使用列表连接运算符(@)。

Return 和 Zero 成员很简单。Return 需要把实际值打包到Logging<‘T> 类型中,Zero 表示不携带任何值的计算(即,返回 unit)。在这两种情况下,我们都将创建新的计算值,因此,基本操作返回空的日志记录缓冲。所有的日志消息将以其它方式产生,并在Bind 成员中追加。如果你看到了到目前为止我们写的代码,没有任何办法,可以创建非空的日志!因此,我们需要创建一个额外的基本操作,以创建包含日志消息的计算值。可以写成一个简单的函数:

 

> let logMessage(s) = 

     Log((), [s]) 

val logMessage : string ->Logging<unit>

 

这个函数创建一个包含 unit 值的计算值,更重要的是,还包含在日志记录的缓冲中的消息,因此,如果我们使用 Bind,把它与另外的计算组合起来,就能得到写到日志中的计算。现在,我们终于可以写代码,使用新创建的日志记录计算。

 

12.7.1 创建日志记录的计算