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



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

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的命令提示符。这会对未使用的"变量"发出警告。

问题出在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

您的绑定操作符可以简单如下:

    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 -> ...))

Bind中的f函数表示在当前绑定之后发生的任何事情。好吧,之后发生的是你在...中使用值a;即在Bind中,必须将f应用于value。然后,f将返回整个计算的结果,我们把它放在value'中,以及累积的日志,我们把它放在log'中。

相关内容

  • 没有找到相关文章

最新更新