就像 Matthieu M. 所说的,这对 C++ 来说是相对较新的东西,但对于许多函数式语言来说并不是什么新鲜事。
我想在这里添加我的 2 美分:在我看来,可以在“程序与功能”方法中找到部分困难和差异。而且我想用Scala(因为我对Scala和C++都很熟悉,而且我觉得它有一个更接近的工具(Option)Expected<T>
)来说明这种区别。
在 Scala 中,您有 Option[T],它是 Some(t) 或 None。特别是,也可能有 Option[Unit],它在道德上等价于Expected<void>
.
在 Scala 中,使用模式非常相似,并且围绕 2 个函数构建:isDefined() 和 get()。但它也有一个“map()”功能。
我喜欢将“map”视为“isDefined + get”的功能等价物:
if (opt.isDefined)
opt.get.doSomething
变成
val res = opt.map(t => t.doSomething)
将选项“传播”到结果
我认为在这里,以这种使用和组合选项的功能风格,可以回答您的问题:
那么,如果你有另一个函数,比如 toUpper(s),它会就地修改字符串并且没有返回值,你的代码会是什么样子?
就个人而言,我不会修改字符串,或者至少我不会返回任何内容。我认为Expected<T>
这是一个“功能性”概念,需要一个功能性模式才能正常工作: toUpper(s) 需要返回一个新字符串,或者在修改后返回自身:
auto s = toUpper(s);
s.get(); ...
或者,使用类似 Scala 的地图
val finalS = toUpper(s).map(upperS => upperS.someOtherManipulation)
如果您不想遵循功能路线,则可以使用 isDefined/valid 并以更程序化的方式编写代码:
auto s = toUpper(s);
if (s.valid())
....
如果您遵循这条路线(可能是因为您需要),则需要说明“void vs. unit”的观点:从历史上看,void 不被视为一种类型,但“无类型”(void foo() 被视为类似于 Pascal程序)。单元(在函数式语言中使用)更多地被视为一种类型,意思是“计算”。所以返回一个 Option[Unit] 确实更有意义,被视为“一个可以选择做某事的计算”。在 中Expected<void>
,void 具有类似的含义:当它按预期工作时(没有例外情况),计算就结束了(什么也不返回)。至少,海事组织!
因此,使用 Expected 或 Option[Unit] 可以被视为可能产生结果的计算,也可能不产生结果。将它们链接起来会很困难:
auto c1 = doSomething(s); //do something on s, either succeed or fail
if (c1.valid()) {
auto c2 = doSomethingElse(s); //do something on s, either succeed or fail
if (c2.valid()) {
...
不是很干净。
Scala 中的 Map 让它更干净一点
doSomething(s) //do something on s, either succeed or fail
.map(_ => doSomethingElse(s) //do something on s, either succeed or fail
.map(_ => ...)
哪个更好,但仍远非理想。在这里,Maybe monad 显然赢了……但那是另一回事了。