Getting Functional With Perhaps

Published Mar 4, 2014.
For feedback or questions, please follow me on Twitter so you can DM me.

Ever since the introduction of LINQ, people have been trying all sorts of clever ways to get more functional constructs into C# to wrap away certain annoying procedural details that are part of the language because of its non-functional beginnings. One of the most annoying class of operations in this context are the TryX methods (e.g. TryGetValue, TryParse and so on) that use out parameters and force you to break into statements what is otherwise a fluent sequence of calls.

Then there are others, even within LINQ, such as FirstOrDefault that keeps you from choking like First does - but then can't tell the difference between something not being there versus something being there but being null (or the type-default for value types).

I recently stumbled across two instances of the clever things people do, viz. Brad Wilson's Maybe<T> and JaredPar's Option<T>. This inspired me to add some more goodness in and cook up something of my own, aptly named Perhaps<T>. I am making it part of the Commons Library, and you can find the source code here:

Here are a few examples of how this might be used:

// Example 1: Dictionaries.
 
 
// Using "string" as key/value for example; could be anything.
IDictionary<string, string> myDictionary;
// ...
// ...
 
// Returns Perhaps<string>.NotThere if not found.
var dictionaryResult = myDictionary.LookFor("MyKey"); 
 
if (dictionaryResult.IsThere)
{
    // Throws if called on "NotThere".
    var string1 = dictionaryResult.Value;
    
    var string2 = dictionaryResult.ValueOrDefault;
    
    // Automatically casts to underlying type.
    var string3 = dictionaryResult + " and so on and so forth.";
 
    // Other stuff to do if the value is there.
}
 
// Look for stuff and operate on it in one sequence of calls.
myDictionary.LookFor("MyKey").DoIfThere(value => /* Do stuff with value */);
 
// Look for stuff and get otehr stuff based on it in one sequence of calls.
var finalValue = myDictionary
    .LookFor("MyKey")
    .DoIfThere(value => /* Return stuff based on value */, "Default value");
 
 
// Example 2: Parsing text.
 
string text;
// ...
 
text.ParseInteger().DoIfThere(value => /* Do stuff with value */);
text.ParseDateTime().DoIfThere(value => /* Do stuff with value */);
// and so on and so forth for long, float, double, decimal, bool
 
// Example 3: Stuff that throws ("int" used as type for example, could be anything).
 
var result = Perhaps<int>.Try(() => /* Stuff that may throw an exception. */);
if (!result.HasError)
{
    // Do stuff here. Now, this may seem weird, but this makes more sense when you think of
    // getting a list of results and filtering out errored ones and then operating further on
    // valid ones, etc.
}
 
 
// Example 4: LINQ stuff ("string" used as type for example, could be anything).
 
IEnumerable<string> list;
// ...
 
var first = list.PerhapsFirst(x => x.ParseInteger().IsThere); // Aaha!
var single = list.PerhapsSingle().DoIfThere(x => /* Do stuff with value */);
var last = list.PerhapsLast(x => x.StartsWith("ABC"));

It's mostly obvious stuff, but I think this will go a long way in making my code less noisy.



Tagged as  csharp dotnet functional