【问题标题】:How to combine Option and Result如何结合选项和结果
【发布时间】:2020-10-02 19:18:36
【问题描述】:

我发现自己经常返回类型Option<Result<'a,'b>>,我的理由如下:

我使用 Option 来决定我是否有兴趣处理某个事件,如果有兴趣,则返回处理结果。

这种模式有合适的名称吗?以及更好的运营商?例如,我使用:Option.map(Result.map f) 是否有映射嵌套函子的运算符?

【问题讨论】:

    标签: f#


    【解决方案1】:

    我认为 Gus 的现有答案回答了您关于模式名称的问题。在 Haskell 中,这是使用 monad 转换器 实现的,而 F#+ 库也允许您在 F# 中执行此操作。

    但是,这种模式在大多数 F# 代码中并不常见。一个原因是它会导致代码非常复杂(两种类型都变得复杂,处理代码也变得非常麻烦)。

    如果您发现自己经常使用Option<Result<'T>>,那么我会考虑为此定义一个新类型是否有意义,也许使用在您的域中有意义的名称,以便代码的读者可以轻松理解它.你可以使用类似的东西:

    type ProcessingResult<'T> = 
      | Ignored
      | Failed of string
      | Accepted of 'T
    

    您将失去OptionResult 的内置函数,但您可以自己实现您需要的函数。也不算太复杂:

    module Processing =
      let map f = function
        | Ignored -> Ignored
        | Failed s -> Failed s
        | Accepted v -> Accepted (f v)
    

    这样做的好处是Processing.map 更容易阅读,我认为这是值得权衡的。

    【讨论】:

    • 谢谢!我总是在某个时候将它写在 Choice1Of3 中,但最终还是回到了Option&lt;Result&lt;'a,'b&gt;,因为它的构图更好;我总是拒绝创建 DU 的想法,因为那时我必须处理中间 fns 中的分支,但如果我重新实现 map,例如,那将不是问题并且会更具体,谢谢你的输入,我也会尝试这种方法,看看结果如何。
    【解决方案2】:

    我称之为一元堆栈。

    您可以定义自己的运算符,但如果要映射它们,则需要类似“深度映射级别 2”之类的东西,通常为 (map &gt;&gt; map),因此,在这种情况下:

    Some (Result<int,string>.Ok 1) |> (Result.map >> Option.map) ((+) 1)
    // val it : Result<int,string> option = Some (Ok 2)
    

    如果您想探索更多,F#+ 通过 2 个不同的抽象对此提供了有限的支持:

    • 组合函子:使用来自FSharpPlus.DataCompose 类型:
    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)
    

    因此,它允许您组合任意 Functor,这些类型是您可以映射的类型。

    • Monad 转换器:在这种特殊情况下使用来自FSharpPlus.DataResultT 类型:

    前面的例子可以工作,只需将Compose 替换为ResultT,但现在你也可以进行一元操作:

    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)
    

    【讨论】:

    猜你喜欢
    • 2012-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多