HiveBrain v1.2.0
Get Started
← Back to all entries
debugMinor

Doing an ROP style bind for two functions on the same input in a pipeline

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
samethedoinginputstyletwoforropbindfunctions

Problem

I am working on understanding Railway Oriented Programming (Scott Wlaschin style) in F#. In my example I want to create a pipeline which does some calculation, applies two different functions to the output of that initial calculation, then hands off a tuple of its results to a final function.

In real code the 'initial calculation' might be getting some data, the 'two different functions' might produce a textual summary and a chart of the data, and the final function might be rendering some output containing both the text and the chart.

I've invented a bind2 function to achieve this, but I can't help thinking that if this was a good idea it would already be common practice and must appear (in disguise) in existing libraries such as Scott's.

Please let me know how I would achieve what I want in an idiomatic style.

Here's my code. All above bind2 is taken broadly from Scott's code, the remainder is my bind2 function and a simple demo.

type Result =
| Success of 'S
| Failure of message : 'F list

let bind f x = 
    match x with 
    | Success s -> f s
    | Failure f -> Failure f

let bind2 (f1 : 'A -> Result)
          (f2 : 'A -> Result) (x : Result) : Result =
    match x with
    | Success _ ->
        let r1, r2 = (bind f1) x, (bind f2) x
        match r1, r2 with
        | Success s1, Success s2 ->
            Success(s1, s2)
        | Failure f1, _ ->
            Failure f1
        | _, Failure f2 ->
            Failure f2
    | Failure f -> Failure f

let demo1 (a: int) : Result =
    Success a

let demo2 (a: int) : Result =
    a |> string |> Success

let demo3 (a: int) : Result =
    a |> (*) -1 |> string |> Success

let demo4 (s1 : string, s2 : string) =
    sprintf "%s - %s" s1 s2 |> Success

let Demo =
    bind demo1
    >> bind2 demo2 demo3
    >> bind demo4

Solution

I actually wouldn't bother defining a bind2 as this can lead to an explosion, bind3, 4, 5, 6. Instead I would define a simple computation builder to do this. So using your code from above.

type DemoBuilder() = 
     member x.Bind(m,f) = bind f m
     member x.Return(s) = Success s

 let demo = DemoBuilder()


which you can then use like so

let DemoB x = 
    demo {
        let! input = x
        let! a = demo1 input
        let! b = demo2 a
        let! c = demo3 a
        return (b,c)
    }

 DemoB (Success 1)

Code Snippets

type DemoBuilder() = 
     member x.Bind(m,f) = bind f m
     member x.Return(s) = Success s

 let demo = DemoBuilder()
let DemoB x = 
    demo {
        let! input = x
        let! a = demo1 input
        let! b = demo2 a
        let! c = demo3 a
        return (b,c)
    }

 DemoB (Success 1)

Context

StackExchange Code Review Q#142054, answer score: 8

Revisions (0)

No revisions yet.