我的记录器计算表达式出了什么问题?

时间:2014-03-17 08:59:31

标签: f# computation-expression

在我试图实现的计算表达式下面。该值包含在元组中,其中元组的第二项是表示沿途日志条目的字符串列表。

type LoggerBuilder() = 
    member this.Bind(vl : 'a * string list, f) = 
        let value = vl |> fst
        let logs = vl |> snd

        let appendLogs logs tpl =
            let value = vl |> fst
            let log = vl |> snd
            (value, logs |> List.append log)             

        (f value) |> appendLogs logs

    member this.Return(x) = 
        (x, [])

然而,当我运行以下内容时,我没有得到预期的结果。我想知道我错过了哪里。

let log = new LoggerBuilder()

let a = log {
    let! a = (1, ["assign 1"])
    let! b = (2, ["assign 2"])
    return a + b
}

// Result:
val a : int * string list = (1, ["assign 1"; "assign 1"])

// Expected:
val a : int * string list = (3, ["assign 1"; "assign 2"])

更新

要避免此错误,请将--warnon:1182传递给fsifsc的命令提示符。这将引发对未使用的“变量”的警告。

2 个答案:

答案 0 :(得分:6)

问题在于appendLogs功能的实现。在那里,您不使用tpl参数,而是使用外部范围vl代替,其中仅包含当前计算部分的值和日志。您还需要翻转List.append的参数,否则日志将会倒退。

通过这两种修复,您的功能将如下所示:

let appendLogs logs tpl =
   let value = tpl |> fst
   let log = tpl |> snd
   (value, log |> List.append logs)

有了这个,你应该得到预期的结果。

我还想通过在第一个方法参数中使用解构以及稍后在let绑定中添加,您的Bind函数可以更简单地实现:

member this.Bind((x, log) : 'a * string list, f) = 
    let y, nextLog = f x
    y, List.append log nextLog

答案 1 :(得分:1)

你的绑定操作符可以是这样的:

    member this.Bind((value, log), f) = 
        let (value', log') = f value
        value', log @ log'

将元组分开的惯用方法是模式匹配它。我避免使用元组的名称vl,而是直接匹配参数列表中的valuelog

其次,回想一下let!绑定被重写:

let! a = (1, ["assign 1"])
...

到此:

LoggerBuilder.Bind((1, ["assign 1"]), (fun a -> ...))

f中的Bind功能代表当前绑定后发生的任何事件。那么,之后发生的是你在a中使用值...;也就是说,在Bind中,您必须将f应用于valuef将返回整个计算的结果,我们将其放在value'中,以及累积的日志中,我们将其放在log'中。