我为什么要使用即将到来的 let! ... 和!句法?

Why would I want to use the upcoming let! ... and! syntax?

language suggestion 指出链接文件中详细介绍了这些优点。我快速浏览了一下,看不到它的明确拼写。

优点是每条语句并行执行所以我可能会提高速度吗? 或者它是否满足某种逻辑,使用通常的 monadic let!?

不方便

我明白,这是适用的,意味着它有一个限制,我不能用前面的表达式来确定后面表达式的逻辑。那么这是否意味着折衷是灵活性换取效率?

我理解 Don Syme's description 的方式,当我前一段时间阅读它时,let! ... and! ... 链中的每一步都会被执行,这与使用 let! ... let! ... 时不同。例如,假设您使用 option CE。那么如果你写

option {
    let! a = parseInt("a")
    let! b = parseInt("b")
    return a + b
}

只有第一个let!会被执行,因为CEshort-circuits一遇到None。改为 let! a ... and! b = ... 将尝试解析两个字符串;虽然不一定是平行的,据我了解。

在解析来自外部源的数据时,我看到了这方面的巨大好处。例如考虑 运行

parse {
    let! name = tryParse<Name>("John Doe")
    and! email = tryParse<EMail>("johndoe@")
    and! age = tryParse<uint>("abc")
    return (name, email, age)
}

并在 return 中得到 Error(["'johndoe@' isn't a valid e-mail"; "'abc' isn't a valid uint"]),而不仅仅是第一个错误。这对我来说非常整洁。

我对此感到兴奋有两个原因。可能会获得更好的性能,并且一些不能适合 Monad 模式的东西可以适合 Applicative Functor 模式。

为了支持已经存在的 let!,需要实现 BindReturn,即 Monad 模式。

let!and! 需要 ApplyPure(不确定 F# 等效项的名称),即 Applicative Functor 模式。

Applicative Functor 不如 Monad 强大,但仍然非常有用。例如,在解析器中。

正如 Philip Carter 所提到的,Applicative Functor 可以比 Monad 更高效,因为它可以缓存它们。

原因是Bind的签名:M<'T> -> ('T -> M<'U>) -> M<'U>.

实现绑定的一种典型方法是“执行”第一个参数以生成值 'T 并将第二个参数传递给它以获取下一步 M<'U> ,您将其执行为嗯。

因为第二个参数是一个函数,可以 return 任何 M<'U> 这意味着缓存是不可能的。

Apply 的签名是:M<'T -> 'U> -> M<'T> -> M<'U>。由于这两个输入都不是函数,因此您可以缓存它们或进行预计算。

这对解析器很有用,例如在 FParsec 中出于性能原因不建议使用 Bind

此外; Bind 的一个“缺点”是,如果第一个输入没有产生值 'T,则无法进行第二个计算 M<'U>。如果您正在构建某种验证器 monad,这意味着一旦由于输入无效而无法生成有效值,则验证将停止,您将不会收到剩余输入的报告。

使用 Applicative Functor,您可以执行所有验证器以生成整个文档的验证报告。

所以我觉得很酷!