Lately I have been playing around with functional programming and F# and I have been trying to apply functional programming to C# as well. Even though C# is not a pure functional programming language, by supporting delegates, lambda method syntax and extension methods, we can produce code written in a functional style without any problems.
But I will not go into the details of explaining functional programing paradigm, but rather I will concentrate on how by using a specific functional structure called monad we can remove a lot of boilerplate code.
We all have seen a code like this:
Example 1:
public void AddStudentToSchool(Guid schoolId, StudentDto studentDto)
{
using (ITransaction transaction = _transactionFactory.Create())
{
_securityService.Validate();
School school = _schoolRepository.GetSchoolById(schoolId);
if(school == null)
{
throw new ResourceNotFoundException("School does not exist");
}
try
{
Student student = new Student(studentDto);
school.AddStudent(student);
}
catch(BusinessRuleException ex)
{
throw new ValidationException(ex.BrokenRules);
}
catch(InvalidStudentException ex)
{
throw new ValidationException(ex.BrokenRules);
}
}
}
Wouldn’t it be more expressive and easier to read and understand the intention if we could write the following instead:
Example 2:
public void AddStudentToSchool(Guid schoolId, StudentDto studentDto)
{
using (ITransaction transaction = _transactionFactory.Create())
{
_securityService.Validate();
School school = _schoolRepository.GetSchoolById(schoolId);
school.IfNullThrowResourceNotFound("School does not exist")
.IfNotNull(() =>
{
Student student = new Student(studentDto);
school.AddStudent(student);
})
.OnException<BusinessRuleException>(ex => throw new ValidationException(ex.BrokenRules))
.OnException<InvalidStudentException>(ex => throw new ValidationException(ex.BrokenRules));
}
}
Well, with the help of extension methods and by borrowing a structure from functional programming called monad we can.
Wikipedia defines the monad as following:
“In functional programming, a monad is a structure that represents computations defined as sequences of steps: a type with a monad structure defines what it means to chain operations, or nest functions of that type together. This allows the programmer to build pipelines that process data in steps, in which each action is decorated with additional processing rules provided by the monad.”
From the following paragraph, we are interested in the sentence “…This allows the programmer to build pipelines that process data in steps …” which is exactly what we want to achieve.
I won’t go into the details of explaining monads, but in essence a monad is a pattern for doing function composition with ‘amplified’ types (as Mike Hadlow puts it very eloquently on his blog series about monads).
Going back to our code in example 1, if we study the code in essence what we trying to do is:
- Get the school from the repository
- If school is null throw an exception
- If school is not null then
- Try to execute more code (by adding the student to the school)
- If an exception is thrown, wrap that exception into ValidationException
To convert the above steps in chain of actions we need to make sure that the output of the previous action will be the input of the next action on the chain. So maybe monad (also known as Option in F#) will be adequate.
Before we dive in with writing code, let’s look at what do we need to implement for our type to be monad. Definition in the Wikipedia states the following:
For a type to be regarded a monad, it needs to have a type constructor M and two operations, bind and return:
- * The return operation takes a value from a plain type and puts it into a monadic container using the constructor, creating a monadic value.
- The bind operation takes as its arguments a monadic value and a function from a plain type to a monadic value, and returns a new monadic value.
Maybe monad is very similar to Nullable<T> type in C#, except our maybe type will be represented by two different types. The Some type which will indicate a presence of a value and None type which will indicate an absence of a value. We will implement our maybe type as an interface and will call it IOptional. So the implementation looks like:
public interface IOptional<T>
{
}
The None type is implemented as :
public class None<T> : IOptional<T>
{
public override string ToString()
{
return "None";
}
}
And Some type is implemented as:
public class Some<T> : IOptional<T>
{
public Some(T value)
{
Value = value;
}
public T Value { get; private set; }
public override string ToString()
{
return Value.ToString();
}
}
Now, to convert our IOptional<T> into a monad we need to implement the Return and Bind methods. We will be implementing them as extension methods. So the implementation looks like:
public static class OptionalExtensions
{
public static IOptional<T> ToOptional<T>(this T value) where T : class
{
if (value == null)
{
return new None<T>();
}
return new Some<T>(value);
}
public static IOptional<T2> Bind<T1, T2>(this IOptional<T1> a, Func<T1, IOptional<T2>> func)
{
var val = a as Some<T1>;
return val != null
? func(val.Value)
: new None<T2>();
}
}
Now our IOptional<T> type is monad. The next step is to start building the methods that we call chain them together. All the methods will be implemented as extension methods also.
The first one is throw if null. So the implementation looks like:
public static IOptional<T> IfNullThrowResourceNotFound<T>(this IOptional<T> value, string message)
where T : class
{
Some<T> some = value as Some<T>;
if (some == null)
{
throw new ResourceNotFoundException(message);
}
return value;
}
The next implementation is to try and execute an action if the value is not null. So the implementation looks like:
public static IOptional<Action> IfNotNull<T>(this IOptional<T> value, Action action)
where T : class
{
return value.Bind(t => new Some<Action>(action));
}
Next we will implement OnException step. The implementation looks like:
public static IOptional<Action> OnException<E>(this IOptional<Action> value, Action<E> action)
where E : Exception
{
return value.Bind(t =>
{
return new Some<Action>(() =>
{
try
{
t();
}
catch (E ex)
{
action(ex);
}
});
});
}
And the last step is the Execute, which executes the code
public static void Execute(this IOptional<Action> value)
{
value.Bind(t =>
{
t();
return new None<bool>();
});
}
Once we have finished implementing the extension methods, our code from example 1 looks like:
School school = _schoolRepository.GetSchoolById(schoolId);
school
.ToOptional()
.IfNullThrowResourceNotFound("School does not exist")
.IfNotNull(() =>
{
Student student = new Student(studentDto);
school.AddStudent(student);
})
.OnException<BusinessRuleException>(ex => { throw new ValidationException(ex.BrokenRules); })
.OnException<InvalidStudentException>(ex => { throw new ValidationException(ex.BrokenRules); })
.Execute();
As you can see, now our code looks a lot neater and easier to read without any boilerplate code. I hope you find this useful.