将铁路导向故障轨道转换为Rx友好错误

时间:2018-04-21 14:36:09

标签: f# functional-programming system.reactive monads monad-transformers

我正在使用一个将结果作为双轨值(成功和失败)的库。在Observable.map函数体中,我经常从函数的成功轨迹中获得一个可观察的,而我不会知道如何处理它们(在Observable.map身体)。

换句话说,我经常陷入结果看起来如下的情况(当然这是最简单的一个):

Rop.Result<IObservable<Rop.Result<IObservable,Messages>,Messages>

一方面,将消息转换回异常并提升它们似乎与我联系在一起,另一方面,错误处理程序是不纯的函数,我宁愿不传递它们。

我想知道在Observable.map机构的双轨结果处理失败的标准和清洁解决方案是什么。

更新:示例

这是问题中最简单的问题:

module problem
open FSharp.Control.Reactive

type Person = {FirstName:string;LastName:string}

let getPersonByLastName lastName =

    let persons =   Observable.toObservable<|seq{
            yield {FirstName="Jhone";LastName="Smith"}
            yield {FirstName="Joe";LastName="Smith"}
            yield {FirstName="Jack";LastName="Smith"}
            }

    Rop.succeed persons

let main =
  let final =
    Observable.single("Smith")
    |>Observable.map(fun lastName -> 
                    let personResults = getPersonByLastName lastName
                    personResults
                    )

  0

在此示例中,表达式的final结果属于IObservable<Result<IObservable<Person>,ErrorMessage list>>类型,但我希望它为IObservable<Person>,因为进一步的Observable转换最终会非常脏复杂的表达。

2 个答案:

答案 0 :(得分:3)

在最简单的层面上,您需要的是提取并展示IObservable中包含的RopResult的方法。评论的一部分已在评论中提及(Observable.bind),另一部分是函数RopResult<IObservable<'a>, 'b> -> IObservable<RopResult<'a,'b>>

要实现这一点,您需要解构RopResult并处理这两种情况。所以把它放在你陈述的问题的背景下:

Observable.single("Smith")
|>Observable.bind(fun lastName -> 
    match getPersonByLastName lastName with
    | Rop.RopResult.Success (next, msgs) -> 
        next |> Observable.map (fun x -> Rop.RopResult.Success (x, msgs))
    | Rop.RopResult.Failure msgs ->
        Observable.result <| Rop.RopResult.Failure msgs)

这是好的代码吗?我不这么认为。你可以通过使用其他ROP api函数或者在这里使用绑定体的计算表达式来使它稍微好一些,但这不是重点。

使用Observables的反应式编程和“面向铁路的编程”都是有用的工具,只要它们能够捕获和抽象出你正在建模的任何计算中涉及的复杂因素。这是有效的,因为它们构建在组合在一起的基元上。当你试图像这样“交错”这两个时,你就会放弃一些,因为你必须自己管理这个组合。

我想在这里说you're better off using exceptions - 特别是因为Observables有内置的支持。

答案 1 :(得分:2)

如果您不使用成功添加消息,将ObservableRopResult结合使用似乎很自然。

exception ROPExn of string list
let combine = function
                         | Success (x, _) -> x
                         | Failure errors -> Observable.Throw(ROPExn(errors))                  
let main =
  let final =  Observable.single("Smith")  |> Observable.bind(getPersonByLastName >> combine)
  let handleError = function
      |ROPExn(list) -> printfn "ROP fail"
      | _ -> printfn "error"
  let subscription = final |> Observable.subscribeWithCallbacks (fun p -> printfn "%s" p.FirstName) handleError ignore