A data flow helper class

One problem I encounter when processing lists is exception handling. I prefer to write code that "chains" calls transforming the data:

  var results = list
    .Select(DoThing1)
    .Select(DoThing2)
    // ...
    .Select(DoThingN)
    .ToList();

The problem with something like this is that, if any of the calls throws an exception, processing stops for the whole list. Handling that requires that I move the "chain" to a new method and handle exceptions there:

  var results = list.Select(InnerMethod).ToList();

  // ...
  private ResultN InnerMethod(Input input)
  {
    try
    {
      var r1 = DoThing1(input);
      var r2 = DoThing2(r1);
      // ...
      var rn = DoThingN(rn_1);
      
      return rn;
    }
    catch(Exception ex)
    {
      // do something with ex, like logging
      return ?? // can't throw, I want to continue processing the rest of the list
    }
  }

Now I have two problems :) One is that the code just looks uglier, so maybe most people can ignore that. (I have OCD with regards to this - code that "looks bad" drives me nuts.) The more important issue is that I need to decide on an "empty" ResultN value to return from the inner method and then I need to be able to filter those out of the overall results. That can get ugly really quickly.

By analogy with what I read about other languages (I think GO uses this approach - I've never studied the language but I believe I first encountered the idea in some articles about it), I decided to write an "either a good value or an exception" helper class. On further reflection I changed that to a struct because I don't want to check that the value is not null. Once I thought of that I also decided that null is not a "good value", so any attempt to pass it as such will result in an ArgumentNullException instead in the "or an exception" part. I hope things will become clearer from the code:

  public struct Result<T>
  {
    public bool HasValue { get; }

    public T Value
    {
      get
      {
        if (!HasValue)
          throw Exception;

        return value;
      }
    }

    public Exception Exception => HasValue ? null : exception ?? NULL_EXCEPTION;

    public Result(T value)
    {
      // do not accept null
      if (value == null)
      {
        HasValue = false;
        this.value = default(T);
        exception = new ArgumentNullException();
      }
      else
      {
        HasValue = true;
        this.value = value;
        exception = null;
      }
    }

    public Result(Exception ex)
    {
      HasValue = false;
      value = default(T);
      exception = ex;
    }

    //

    // ReSharper disable once StaticMemberInGenericType
    private static readonly Exception NULL_EXCEPTION = new ArgumentNullException();

    private readonly T value;
    private readonly Exception exception;
  }

I also added an Apply extension method - the reason it's an extension method instead of a method in the original struct is because of the additional generic type TR; it just looked wrong there. (I did mention my OCD, right?)

  public static class ResultExtensions
  {
    public static Result<TR> Apply<T, TR>(this Result<T> it, Func<T, TR> selector)
    {
      return it.HasValue
        ? Try(() => selector(it.Value))
        : new Result<TR>(it.Exception);
    }

    public static IEnumerable<Result<TR>> Select<T, TR>(this IEnumerable<Result<T>> list, Func<T, TR> selector)
    {
      return list.Select(it => it.Apply(selector));
    }

    //

    private static Result<TR> Try<TR>(Func<TR> func)
    {
      try
      {
        return new Result<TR>(func());
      }
      catch (Exception ex)
      {
        return new Result<TR>(ex);
      }
    }
  }

While I didn't write this in a TDD fashion, I have added some asserts to a console application to make sure I got back the expected results:

  static class Program
  {
    static void Main()
    {
      var r1 = Divide(5, 2);
      Debug.Assert(Print(r1) == "HasValue = True Value = 2 Exception = ");
      var r2 = Divide(5, 0);
      Debug.Assert(Print(r2) == "HasValue = False Value = (invalid) Exception = Attempted to divide by zero.");
      var r3 = new Result<int?>(3);
      Debug.Assert(Print(r3) == "HasValue = True Value = 3 Exception = ");
      var r4 = new Result<int?>((int?) null);
      Debug.Assert(Print(r4) == "HasValue = False Value = (invalid) Exception = Value cannot be null.");
      var r5 = new Result<object>(null);
      Debug.Assert(Print(r4) == "HasValue = False Value = (invalid) Exception = Value cannot be null.");

      // using the default constructor
      var r6 = new Result<int>();
      Debug.Assert(Print(r6) == "HasValue = False Value = (invalid) Exception = Value cannot be null.");

      // trying to access the Value property without checking first will result in an exception
      try
      {
        Console.WriteLine(r5.Value);
      }
      catch
      {
        Console.WriteLine("Oops.");
      }

      // we can now chain selectors

      // case 1: all good
      var i1 = new Result<int?>(5);
      var ri1 = i1
        .Apply(it => 100 / it)
        .Apply(it => 200 / it)
        .Apply(it => 10 / it);
      Debug.Assert(Print(ri1) == "HasValue = True Value = 1 Exception = ");

      // case 2: something bad happens
      var i2 = new Result<int>(200);
      var ri2 = i2
        .Apply(it => 100 / it)
        .Apply(it => 200 / it)
        .Apply(it => 10 / it);
      Debug.Assert(Print(ri2) == "HasValue = False Value = (invalid) Exception = Attempted to divide by zero.");

      // finally, the target use case: processing a list without aborting due to exceptions
      var list1 = new List<int> { 10, 20, 0, 30, 40 };
      var list2 = list1
        .Select(it => new Result<int>(it))
        .Select(it => it / 2)
        .Select(it => 10 / it)
        .Select(it => 100 / it)
        .ToList();
      var good = list2.Where(it => it.HasValue).ToList();
      var bad = list2.Where(it => !it.HasValue).ToList();

      Debug.Assert(good.Count == 2);
      Debug.Assert(bad.Count == 3);
    }

    private static Result<int> Divide(int a, int b)
    {
      try
      {
        return new Result<int>(a / b);
      }
      catch (Exception ex)
      {
        return new Result<int>(ex);
      }
    }

    private static string Print<T>(Result<T> r)
    {
      return $"HasValue = {r.HasValue} Value = {(r.HasValue ? r.Value + "" : "(invalid)")} Exception = {r.Exception?.Message}";
    }
  }

Note that the addition of the Select extension method, I didn't have to write the last example as

      var list2 = list1
        .Select(it => new Result<int>(it))
        .Select(it => it.Apply(x => x / 2))
        .Select(it => it.Apply(x => 10 / x))
        .Select(it => it.Apply(x => 100 / x))
        .ToList();

Avoiding boilerplate code is good; so is the fact that the inner lambda doesn't have to know anything about the Result<T> type and yet, any crash in it doesn't abort processing the entire list.

Comments

Popular posts from this blog

Posting dynamic Master / Detail forms with Knockout

Comparing Excel files, take two

EF Code First: seeding with foreign keys