我认为 Gus 的现有答案回答了您关于模式名称的问题。在 Haskell 中,这是使用monad 转换器实现的,而 F#+ 库也允许您在 F# 中执行此操作。
但是,这种模式在大多数 F# 代码中并不常见。原因之一是它会导致非常复杂的代码(两种类型都变得复杂,处理代码变得非常繁琐)。
如果您发现自己Option<Result<'T>>
经常使用,那么我会考虑为此定义一个新类型是否有意义,也许使用在您的域中有意义的名称,以便代码的读者可以轻松理解它。你可以使用类似的东西:
type ProcessingResult<'T> =
| Ignored
| Failed of string
| Accepted of 'T
Run Code Online (Sandbox Code Playgroud)
您将失去Option
and 的内置函数Result
,但您可以实现自己需要的函数。它并不太复杂:
module Processing =
let map f = function
| Ignored -> Ignored
| Failed s -> Failed s
| Accepted v -> Accepted (f v)
Run Code Online (Sandbox Code Playgroud)
这样做的好处Processing.map
是更容易阅读,我认为这是一个值得做出的权衡。
我会称它为一元堆栈。
你可以定义你自己的操作符,但如果你想映射它们,你需要像“深度映射级别 2”(map >> map)
这样的东西,在这种情况下通常是这样:
Some (Result<int,string>.Ok 1) |> (Result.map >> Option.map) ((+) 1)
// val it : Result<int,string> option = Some (Ok 2)
Run Code Online (Sandbox Code Playgroud)
如果您想探索更多F#+通过 2 个不同的抽象为此提供有限的支持:
Compose
类型 from FSharpPlus.Data
:let v1 = Compose (Some (Result<int,string>.Ok 1))
let v2 = v1 |> map ((+) 1)
// .. more operations, then when you're finished ..
let v3 = Compose.run v2
// val v1 : Compose<Result<int,string> option> = Compose (Some (Ok 1))
// val v2 : Compose<Result<int,string> option> = Compose (Some (Ok 2))
// val v3 : Result<int,string> option = Some (Ok 2)
Run Code Online (Sandbox Code Playgroud)
所以它允许你组合任意的 Functor,这是你可以映射的类型。
ResultT
类型FSharpPlus.Data
:前面的示例可以工作,只需替换Compose
for ResultT
,但现在您也可以执行 monadic 操作:
let x = monad' {
let! v1 = ResultT (Some (Result<int,string>.Ok 1))
let! v2 = ResultT (Some (Result<int,string>.Ok 1))
return v1 + v2 }
ResultT.run x
// val x : ResultT<Result<int,string> option> = ResultT (Some (Ok 2))
// val it : Result<int,string> option = Some (Ok 2)
Run Code Online (Sandbox Code Playgroud)