3

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

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

4

2 回答 2

7

问题在于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
于 2014-03-17T09:13:29.207 回答
1

您的绑定运算符可以是这样的:

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

将元组分开的惯用方法是对其进行模式匹配。我避免使用vl元组的名称,而是直接匹配到value参数log列表中。

其次,回想一下let!绑定是从这里重写的:

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

对此:

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

您中的f函数代表当前绑定之后Bind发生的任何事情。好吧,之后发生的是您使用;中的值。也就是说,在 中,您必须向申请。然后将返回整个计算的结果,我们把它放进去,以及积累的日志,我们把它放进去。a...Bindfvaluefvalue'log'

于 2014-03-17T09:12:20.453 回答