我为什么要使用即将到来的 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!
,需要实现 Bind
和 Return
,即 Monad 模式。
let!
和 and!
需要 Apply
和 Pure
(不确定 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,您可以执行所有验证器以生成整个文档的验证报告。
所以我觉得很酷!
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!
,需要实现 Bind
和 Return
,即 Monad 模式。
let!
和 and!
需要 Apply
和 Pure
(不确定 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,您可以执行所有验证器以生成整个文档的验证报告。
所以我觉得很酷!