In the last part I created a Maybe monad for C#, a type that allows you to indicate a lack of a value. I pointed out it is like Nullable but better.
If you missed the story so far:
Looking back I could have taken another route for the Maybe, I could have used an IEnumerable that could contain either 0 or 1 values. The signature would have been like this:
public interface IMaybe<T> : IEnumerable<T>
{
bool HasValue { get; }
T Value { get; }
TResult Match<TResult>(
Func<T, TResult> someSelector,
Func<TResult> noneSelector);
IMaybe<T> Recover(T defaultValue);
IMaybe<T> Recover(Func<T> defaultFactory);
void Do(Action<T> action);
}
This actually has the benefits that the Select/SelectMany parts of the API come for free but with the cost that you could get strange IEnumerable manipulations that are undesirable so I believe the extra code is a good trade off.
So time to explore the use case for this. First there are the changes to how you store data in your objects. If you have any properties that could be Nullable you should change the type to a Maybe.
public class Person
{
public string Title {get;set;}
public string Forename {get;set;}
public string Surname {get;set;}
IMaybe<Address> HomeAddress {get;set;}
IMaybe<Address> WorkAddress {get;set;}
}
I know the address part is a little contrived, it would be better to have a collection and tag their type but this shows how use of IMaybe adds a great deal of semantic information to the object. You can look and instantly see what is optional and what is required.
Time for exploring some interaction.
var homeCity = person.HomeAddress
.Select(x => x.City)
.Recover("Unknown")
.Value;
person.HomeAddress
.Where(address => address.City == "Mega City One")
.Do(PrintLetter);
The first tries to select the city value from home address, if not present it defaults it then extracts the value. That is a mountain of functionality hidden away. I know the new ?. syntax coupled with ?? can do similar but this offers a level of composability that is missing with that sort of syntax.
The second example will call the action PrintLetter(address) the address exists and the city is the required city, again no need for control flow semantics, it is all built into the workflow and hidden away.
In both cases it is clear what is going on, once you understand the semantics of IMaybe, so the code is reduced to nice clean workflow.
Maybe is a simple Monad but things get more interesting as we move on to things like Either, IO and similar. The beauty is though that all operate on the same map/fmap and reduce syntax from the functional world.
Feel free to ask questions.
Happy coding
Woz