Try class: Handling exception in a functional way with C# 7

While watching a Coursera course of functional programming, I saw a Scala class that I liked in concept and implementation. It's a utility class and is called Try. Now that C# 7 includes pattern matching I thought it was time to review it and pass it to C#.

The Try class represents the result of a function with the expected value or an exception. The purpose is that you can leave your clean code exception management and errors.

But, previous to talk in details about Try, let me to do an introduction to Monad and other implementations.

Monad, Maybe and Try

First, all this term have relation with functional programming, and the way to resolve problems with this paradigm. To an introduction to the topic, you can follow this link.

Monad When using sequenced operations, with monad you can structure the code. Another term most descriptive to this is computation builder. The monad allows us to create pipelines that process data in steps, with additional process rules provided by the monad. Also, can be seen as a functional design pattern for building generic types.

Maybe<T> is a container of type T that may be present or not. It's a monad implementation. It has only one implementation.

Try<T> represents an operation. If it's successful return T, else some subclass of 'Exception'. As Maybe, it's a monad implementation. You create two different subType of Try, Success<T> when the operation is Ok, and Failure<T> when found errors or handling exceptions. Internally, the Failure contains the exception.

Problem

First, to understand the need, I will present the original scenario with an elementary example. We have a function that finds data in the database and then passes that data to another process as arguments. Simple, but a pipeline. For simplicity, it'll be synchronous.

var dbResult = repository.GetData("parameter");  
var result = anotherProcess(dbResult);  

The code above is not bad, but it's too much optimistic. The search operation can at least give a timeout. When you start programming with try-catch block, the code is no longer expressive, as the next code.

customer dbResult;  
try  
{
   dbResult = repository.GetData("parameter");
}
catch (TimeOutException toEx)  
{
   //cut to simplify
}
catch (Exception toEx)  
{
   //cut to simplify
}
var result = anotherProcess(dbResult);  

Solution

The Try class helps us to encapsulate the result and the exception, and whether it is one thing or another is as easy as it is a concrete class or another.

The syntax we use when using a try is as follows:

Try t = f1(x);  
Try y = f2(t);  

So, in the end, is a monad, as the class Maybe, where f1 would be the ToMaybe and f2 would be the projection of the person's address

Maybe personaMaybe = persona.ToMaybe();  
Maybe direccionMaybe = personaMaybe.Select(p=>p.Direccion);  

Like the class Maybe can be put in the chain of operations or pipelines, and catching the exception in the same chain. In the case that was an exception, we have to ensure that the first exception is encapsulated and don't lose the reason and callStack of the original exception.

Implementing Try

The implementation has three parts, an ITry interface, one class that represents the successful result and another class representing a failure

public interface ITry<T>  
{
    T Value { get; }

    ITry<U> Select<U>(Func<T, ITry<U>> mapper);
}

In this case, i add only one operation - Select - to make a mapping between to types.

In the case of the class that represents the failure, the exception comes by the constructor

public class Failure<T> : ITry<T>  
{
    private readonly Exception ex;
    public Failure(Exception ex)
    {
        this.ex = ex;
    }

    public T Value
    {
        get { throw new InvalidOperationException("Can't get value for failed Try"); }
    }

    public ITry<U> Select<U>(Func<T, ITry<U>> mapper)
    {
        return ex.ToFailed<U>();
    }

    public Exception GetException()
    {
        return ex;
    }
}

Please, take a minute to see the Select method.This method is inside the failure class, then don't execute the mapper, on the contrary, the function returns the same exception that was passed in the constructor. Thereby you don't miss the exception that causes the failure, nor the callStack.

public class Success<T> : ITry<T>  
{
    public Success(T newValue)
    {
       Value = newValue;
    }
    public T Value { get; private set; }

    public ITry<U> Select<U>(Func<T, ITry<U>> mapper)
    {
        if (mapper == null)
        {
            return new Failure<U>(new ArgumentNullException(nameof(mapper)));
        }
        try
        {
           return mapper(Value);
        }
        catch (Exception ex)
        {
            return new Failure<U>(ex);
        }
    }
}

The last step, create a extension class to simplify the creation of Failure or Success instance.

public static class TryExtension  
{
    public static ITry<T> ToSucess<T>(this T data)
    {
        return new Success<T>(data);
    }

    public static ITry<T> ToFailed<T>(this Exception ex)
    {
        return new Failure<T>(ex);
    }
}

In the client side, you must know if you had success or not the operations. For this reason, you need patterns matching, the new feature in C#7. The result would be something like this.

var searchResult = customerRepository.GetCustomer(index);  
var processResult = ProcessData(searchResult);

switch (processResult)  
{
    case Success<CustomerDTO> success:
    {
        Console.WriteLine(success.Value.CustomerId);
        break;
    }
    case Failure<CustomerDTO> fail:
    {
        Console.WriteLine(__aSyNcId_<_CqrpbvfT__quot;Problems when get customer. Error : { fail.GetException().Message}");
        break;
    }
}

The switch command can be replaced by if command since we have only two options.

All the source code you can be found on Github.

Conclusion

One important property of Try class is the ability to pipeline or chain, operations and caching exceptions along the way. This class provides some default behaviour in the case of failure. It's error handling, in a functional way.

The last changes on C# 7 allow us to access some high-level features to apply functional programming.

Picture try again by Sean MacEntee, is licensed under Creative Commons BY-2.0..