# Railway Oriented Programming II

In the [last post](https://lightw8.blog/railway-oriented-programming), I talked at a high-level about Scott Wlaschin's "Railway Oriented Programming" (ROP)<sup>1 </sup> analogy for applying a functional style to things like error checking and exception handling.

Here, I'd like to enumerate some of the code samples I built in C# as I "coded along" at home. I haven't yet compared these to any other online code samples on the topic, but you can check my work and see some extra resources on the topic on his ROP page [here](https://fsharpforfunandprofit.com/rop/). There, he also issues a warning that this approach isn't always recommended, and has [some downsides](https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/), too. I'll leave that analysis for another day.

## Error-generating functions

An error-generating function acts as a "switch." A `TIn` input yields either a success type `TOut` output (the normal "happy path" output), or an `Error`:

```csharp
SuccessOrError ErrorGeneratingFunction(TIn input) { ... }
```

Instead of returning a "lowest common denominator" C# `object` type which could contain either result, we can use a discriminated union or "choice" type to represent the output with strong(er) typing. Here's a minimal implementation of an "A or B" `Choice` type:

```csharp
public class Choice<A, B>
{
    public Choice(A value) { Value = value; }
    public Choice(B value) { Value = value; }

    public dynamic Value { get; }

    // equality and string representation delegated to member (Value)
    public override bool Equals(object? obj) => Value.Equals(obj);
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
}
```

I say "stronger" typing because the `Value` parameter is still dynamically typed. (True Discriminiated Unions, a core feature of F#, [appear to be coming in C#](https://github.com/dotnet/csharplang/issues/113) , which is exciting.) With this in place, the `Result` type becomes:

```csharp
// Result implementation
class Result<TOut> : Choice<TOut, Error>
{
    public static implicit operator Result<TOut>(TOut value) => new Result<TOut>(value);
    public static implicit operator Result<TOut>(Error error) => new Result<TOut>(error);

    public Result(TOut value) : base(value) { }
    public Result(Error error) : base(error) { }
}
```

As a convenience, I added implicit conversions from either of the two individual output types (`TOut` and `Error`, here), into the `Result<TOut>` type.

Using this structure, the signature of the error-generating switch function becomes:

```csharp
// switch function signature in Func-form
Func<TIn, Result<TOut>> switchFunction; 

// ...and an example method
Result<float> MaybeInverse(float val) => val switch
{
    0f => new Error("Inverse!"),
    _  => (Result<float>)(1 / val)
};
```

## Bind

Bind is an "adapter block" that glues these error-generating functions together into a two-track model, so that the happy paths compose, and the failure path short-circuits by forwarding any previous Errors.

```csharp
// Bind signature in Func-form
Func<Func<TIn, Result<TOut>>, Func<Result<TIn>, Result<TOut>> bind;

// ...and implemented as a method
Func<Result<TIn>, Result<TOut>> Bind<TIn, TOut>(Func<TIn, Result<TOut>> switchFunction)
{
    return choice => choice.Value switch
    {
        Error e => (Result<TOut>)e,
        TIn tIn => switchFunction(tIn).Value switch
        {
            Error eOut => eOut,
            TOut tOut  => tOut
        }
    };
}
```

## Map

A map is an adapter block that turns (or "lifts") a 1-track function into a two-track function<sup>2</sup>:

```csharp
// Map signature in Func-form
Func<Func<TIn, TOut>, Func<Result<TIn>, Result<TOut>> map;

// ...and implemented as a method (in terms of bind)
Func<Result<TIn>, Result<TOut>> Map<TIn, TOut>(Func<TIn, TOut> oneTrackFunction)
{
    return Bind<TIn, TOut>(input => oneTrackFunction(input));
}
```

## Tee

Tee is an adapter block that turns a "dead-end" function (or Action) into a one-track function:

```csharp
// Tee signature in Func-form
Func<Action<TIn>, Func<TIn, TIn>> tee;

// ...and implemented as a method
Func<TIn, TIn> Tee<TIn>(Action<TIn> deadEndFunction)
{
    return (TIn input) => { deadEndFunction(input); return input; };
}
```

These are "side-effect" functions that might write to the database, send an email, log an action, print to screen, etc. Now that we have a one-track function, we can use Map to turn it into a two track function. Let's call that composite capability "Audit":

## Audit

```csharp
// Audit signature in Func-form
Func<Action<TIn>, Func<Result<TIn>, Result<TIn>>> audit;

// ...and implemented as a method (in terms of Map and Tee)
Func<Result<TIn>, Result<TIn>> Audit<TIn>(Action<TIn> deadEndFunction)
{
    return Map(Tee(deadEndFunction));
}

// ...or alternatively, directly in terms of Bind
Func<Result<TIn>, Result<TIn>> Audit<TIn>(Action<TIn> deadEndFunction)
{
    return Bind<TIn, TIn>(input => { deadEndFunction(input); return input; });
}
```

A supervisory or "audit" function is transformed to a two-track function by just passing Success and Failure inputs straight through to the outputs, and performing Actions on them along the way as needed.

lightw8 comment: You could imagine an enhancement to the Tee method to return a Failure (instead of void), if the action fails. But, then it's "just" an error-generating function (a good thing!), and we already have Bind for that. Speaking of which...

## Exceptions

In this model the guidance is to "catch and don't release" exceptions that are relevant to this level of the computation, and return them instead as a formal Failure result. It's a judgement call about which exceptions to let propagate up the stack, but we should handle things that are reasonably in-scope. We don't want the code blowing up for things we have context for fixing/handling, but we also don't want to guess at the intent of the caller.

## Validation, Events, Undo, Retries

Around minute 37 of the talk, Mr. Wlaschin talks about parallel validation logic, and this leads to a general discussion on how we can augment the `Result` structure to contain additional information on the *success* path, such as store/forward event data and allow for rollback/undo types of actions. Similarly, enhancing the error path can be used to enable retries in the case of errors. I won't elaborate further for now, but this might be interesting to revisit sometime in more detail.

## Final thoughts

I enjoyed the challenge of creating these implementations without checking outside sources. So caveat emptor - no guarantees your trains will arrive on time with this code! In the next post, I'll work through a few examples of how we can use these functional building blocks in practice.

Thanks for reading, and talk soon. -David

<sup>1 </sup> Concept and header image credit to Scott Wlaschin [here](https://fsharpforfunandprofit.com/rop/)

<sup>2</sup> C# type inference looks like it will be [improving in C# 10](https://kenbonny.net/introducing-csharp-10), but for now, we need to explicitly specify the generic types when calling `Bind<TIn, TOut>(...)`
