Convert Railway oriented failure track to Rx friendly errors

非 Y 不嫁゛ 提交于 2019-12-08 01:52:39

问题


I'm using a library that takes the results as two-track values (success and failure).In the Observable.map function bodies I often get an observable from success track of a function, and I don't know how to handle them (at Observable.map body).

In the other words I often get stuck in situations that the result looks something like the following (of course it's the simplest one):

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

On one hand, translating messages back to exceptions and raising them seems wired to me, and on the other hand, the error handlers are impure functions and I'd rather not pass them around.

I wonder what is the standard and clean solution to handle the failures from two-track results at the Observable.map body.

Update: Example

This is the simplest ever sample of the problem :

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

In this example the final result of the expression is of type IObservable<Result<IObservable<Person>,ErrorMessage list>> but I want it to be IObservable<Person>, because further Observable transformations will end up pretty dirty and complicated expressions.


回答1:


On the simplest level, what you need here is a way to extract and then surface an IObservable that is wrapped in a RopResult. One part of the solution was already mentioned in the comments (Observable.bind), the other is a function RopResult<IObservable<'a>, 'b> -> IObservable<RopResult<'a,'b>>.

To get that, you need to deconstruct the RopResult and handle both the cases. So to put that in context of your stated problem:

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)

Is this good code though? I don't think so. You could make it slightly better perhaps by using other ROP api functions, or by using a computation expression for the body of the bind here, but that's not the point.

Both reactive programming using Observables, and "railway-oriented programming" are useful tools as long as they can capture and abstract away some element of complexity involved in whatever computation you're modelling. This works because they're build on primitives that compose well together. When you try to "interleave" the two like this, you forfeit some of that as you have to manage that composition yourself.

I would say you're better off using exceptions here - especially since Observables have a built-in support for them.




回答2:


If you don't use success with message it seems pretty natural to combine Observable and RopResult.

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


来源:https://stackoverflow.com/questions/49956854/convert-railway-oriented-failure-track-to-rx-friendly-errors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!