Click here to Skip to main content
Click here to Skip to main content

Chained null checks and the Maybe monad

By , 21 Sep 2010
 

Introduction

A great many programmers have met a situation where, while accessing a nested object property (e.g., person.Address.PostCode), they have to do several null checks. This requirement frequently pops up in XML parsing where missing elements and attributes can return null when you attempt to access them (and subsequently trying to access Value throws a NullReferenceException). In this article, I'll show how a take on the Maybe monad in C#, coupled with the use of Extension Methods, can be used to improve readability.

Problem Description

So, to start with, let's look at the way to get a person's post code (just imagine you're working with XML or something). The code shown below does several null checks, and assigns the value only if it is available.

string postCode = null;
if (person != null && person.Address != null && 
    person.Address.PostCode != null)
{
  postCode = person.Address.PostCode.ToString();
}

What you've got up there is some fairly unreadable (and un-maintainable) code. Actually, we're lucky to have all of our code fall under a single if - something that might not be possible in a more complex scenario. Let's imagine a more complicated situation - say, we need to perform some operation between the if evaluations. What do we get? That's right - a chain of ifs.

string postCode;
if (person != null)
{
  if (HasMedicalRecord(person) && person.Address != null)
  {
    CheckAddress(person.Address);
    if (person.Address.PostCode != null)
      postCode = person.Address.PostCode.ToString();
    else
      postCode = "UNKNOWN";
  }
}

The code presented above contains a lot of excess data - for example, person.Address.PostCode is mentioned twice. There's nothing incorrect about the code per se, it just has a bit too many symbols. To sum up, we want our code to communicate better that:

  • If the value is null, no further evaluations should be done; if the value is not null, then this is the value we're going to work with.
  • If we perform some action, it only happens on a valid object.

So what am I suggesting? I propose that we create a fluent interface that will satisfy the above conditions without any nesting. To do that, we are going to employ the Maybe monad.

For those of you who know F#, the Maybe monad will be familiar as the Option type. For C# developers, let's just assume that you can have a variable that either has some value or no value (none). Of course, C# doesn't directly support this none-some duality except by using null. Which is precisely why I'm proposing the chained extension solution presented below.

With

Our primary concern is to do the null checks to 'shorten' them so they don't pollute our code. For that, we'll define a With() extension method:

public static TResult With<TInput, TResult>(this TInput o, 
       Func<TInput, TResult> evaluator)
       where TResult : class where TInput : class
{
  if (o == null) return null;
  return evaluator(o);
}

The above method can be attached to any type (because TInput is effectively object). As a parameter, this method takes a function which defines the next value in the chain. If we pass null, we get null back. Let's rewrite our first example using this method:

string postCode = this.With(x => person)
                      .With(x => x.Address)
                      .With(x => x.PostCode);

I suppose, in the above example, we could replace Func<> with Expression<> and try to pull the properties, but I've seen this done, and the resulting code is too slow, and it's also somewhat limiting - it assumes that you're working with just one object, whereas my Maybe chains can (and do) drag in many objects.

Return

Here comes another piece of syntactic sugar - the Return() method. This method will return the 'current' value just like Where() does, but in case null was passed, it will return a different value that we supply. Consider this a kind of a «Where() with fallback» method.

public static TResult Return<TInput,TResult>(this TInput o, 
       Func<TInput, TResult> evaluator, TResult failureValue) where TInput: class
{
  if (o == null) return failureValue;
  return evaluator(o);
}

So let's assume now that, with the absence of a postcode, we want to return, say, string.Empty. Here's how:

string postCode = this.With(x => person).With(x => x.Address)
                      .Return(x => x.PostCode, string.Empty);

By the way, you could rewrite the Extension Method so that failureValue would also be computed via a Func<> - I am yet to meet a scenario where this is required, though. It is typically the case that we never know at which stage the chain failed (and yielded null), so the terminal Return() is typically an indicator (either true/false or null/not null).

If & Unless

Going through the call chain, you sometimes need to do checks not related to null. Theoretically, you could suspend the chain and use an if, or you could use an if in one of the delegates, but... you can simply define an If() extension method (and an Unless() if you feel like it) and plug it into the chain:

public static TInput If<TInput>(this TInput o, Func<TInput, bool> evaluator) 
       where TInput : class
{
  if (o == null) return null;
  return evaluator(o) ? o : null;
}
 
public static TInput Unless<TInput>(this TInput o, Func<TInput, bool> evaluator)
       where TInput : class
{
  if (o == null) return null;
  return evaluator(o) ? null : o;
}

Do

Seeing how we're having a party here, let's add yet another method that simply calls a delegate - and that's it. Of course, this method is best used for one-line calls, and not for evaluating 20-line algorithms with convoluted logic. Nevertheless, the call is quite useful in practice.

public static TInput Do<TInput>(this TInput o, Action<TInput> action) 
       where TInput: class
{
  if (o == null) return null;
  action(o);
  return o;
}

So, we're done: we've got the infrastructure we need to get our post code extraction to be a bit more readable. Here is the end result:

string postCode = this.With(x => person)
    .If(x => HasMedicalRecord(x))
    .With(x => x.Address)
    .Do(x => CheckAddress(x))
    .With(x => x.PostCode)
    .Return(x => x.ToString(), "UNKNOWN");

As you can see, the depth of nesting has fallen to zero - no more curly braces!

Discussion

I use these Maybe-monadic-chain-null-extension-methods (call them how you will) in my R2P software product. Here's an example of a real-life use of these constructs:

public override void VisitInvocationExpression(IInvocationExpression expression)
{
  base.VisitInvocationExpression(expression);
  string typeName = this.With(x => expression)
    .With(x => x.InvokedExpression)
    .With(x => x as IReferenceExpression)
    .With(x => x.Reference)
    .With(x => x.Resolve())
    .With(x => x.DeclaredElement)
    .With(x => x.GetContainingType())
    .Return(x => x.CLRName, null);
  this.If(x => Array.IndexOf(types, typeName) != -1)
    .With(x => ExpressionStatementNavigator.GetByExpression(expression))
    .Do(x =>
          {
            var suggestion = new SideEffectSuggestion(typeName);
            var highlightInfo = new HighlightingInfo(
              expression.GetDocumentRange(),
              suggestion);
            context.HighlightingInfos.Add(highlightInfo);
          });
}

I have to point out here that, at any point, you can stop the chain and start a new one. Why would you want that? Well, for example, you cannot define shared variables within the chain (unless you refactor it all to have a Dictionary<string,object>-like parameter).

By the way, quite frequently, I find myself making additional, domain-specific methods to plug into this chain. For example:

public static IElement IsWithin<TContainingType>(this IElement self) 
       where TContainingType: class, IElement
{
  if (self == null) return self;
  var owner = self.GetContainingElement<TContainingType>(false);
  return owner == null ? self : null;
}

One more thing: this type of notation is actually light obfuscation because, as I'm sure you've guessed, each Extension Method call will be shown as a static method call in Reflector:

public override void VisitInvocationExpression(IInvocationExpression expression)
{
    base.VisitInvocationExpression(expression);
    string typeName = this.With<SideEffectAnalyser, IInvocationExpression>(
    delegate (SideEffectAnalyser x) {
        return expression;
    }).With<IInvocationExpression, ICSharpExpression>(delegate (IInvocationExpression x) {
        return x.InvokedExpression;
    }).With<ICSharpExpression, IReferenceExpression>(delegate (ICSharpExpression x) {
        return (x as IReferenceExpression);
    }).With<IReferenceExpression, IReference>(delegate (IReferenceExpression x) {
        return x.Reference;
    })
    .
    .
    .
    // and so on
}

This approach is easily extensible - for example, a colleague of mine does try-catch checks in his chains, too. Hey, this is kind of like AOP, but without post-build or dynamic proxies. Oh, and the performance hit for these chains is negligible compared to if statements.

That's it! Comments are, as always, welcome!

Update 1: I got a question on how to propagate value types through this hierarchy. This is easy: all you have to do is create another chain method that doesn't do the null check:

public static TResult WithValue<TInput, TResult>(this TInput o, 
       Func<TInput, TResult> evaluator)
       where TInput:struct
{
  return evaluator(o);
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dmitri Nеstеruk
Founder ActiveMesa
United Kingdom United Kingdom
Member
I work primarily with the .NET technology stack, and specialize in accelerated code production via code generation (static or dynamic), aspect-oriented programming, MDA, domain-specific languages and anything else that gets products out the door faster. My languages of choice are C# and F#, though I'm open to suggestions.
 
I'm a Microsoft MVP (Visual C#) since 2009. I run a collective tech blog at DevTalk.net. I use my own editor called TypograFix to typeset articles and blog posts.
 
Like the article and want this implemented in your product? Got a project that can benefit from Microsoft.Net goodness? Then get in touch!

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionVote of 5memberNaerling10 Feb '13 - 0:10 
Just very nice! Thumbs Up | :thumbsup:
It's an OO world.
public class Naerling : Lazy<Person>{
    public void DoWork(){ throw new NotImplementedException(); }
}

GeneralMy vote of 5memberBC @ CV11 Jan '13 - 10:58 
Elegant simplicity!
QuestionI voted 5.memberRob Lyndon1 Jan '13 - 6:42 
Excellent work Dmitri. Definitely one I intend to use.
 
Would To() be a more descriptive name than With()?
 
-- Rob
QuestionAlternative...mvpPaulo Zemek13 Jun '12 - 5:38 
In your sample, you wrote this:
string postCode;
if (person != null)
{
  if (HasMedicalRecord(person) && person.Address != null)
  {
    CheckAddress(person.Address);
    if (person.Address.PostCode != null)
      postCode = person.Address.PostCode.ToString();
    else
      postCode = "UNKNOWN";
  }
}
 
I will say that I prefer to use the normal if and its inner blocks than the alternative you presented. But to avoid excessive access, I will program it more like this:
 
string postCode;
if (person != null)
{
  var address = person.Address;
  if (HasMedicalRecord(person) && address != null)
  {
    CheckAddress(address);
    
    var personPostCode = person.Address.PostCode;
    if (personPostCode != null)
      postCode = personPostCode.ToString();
    else
      postCode = "UNKNOWN";
  }
}
 
The basic idea is... if the "path" is used twice, store it in a local variable before, then use the variable as many times as needed.
And I can even say that if person is null, then the postCode variable is not initialized... if it need to be initialized, I will declare the postCode variable as:
string postCode = "UNKNOWN";
 
And will simple don't have an else to set the postCode as unknown.
QuestionSimilar ideas, but different function namesmemberMichael Freidgeim1 Jun '12 - 13:54 
I found another article The Maybe Monad in C#
[^]
 
Where names of extensions IfNotNull and Maybe are more descriptive.
Michael Freidgeim.
Blog: http://geekswithblogs.net/mnf/

QuestionSource?memberJason Vogel18 Apr '12 - 18:21 
Is there a link to the consolidated source or it is just copy from each section?
AnswerRe: Source?memberDmitri Nesteruk18 Apr '12 - 19:24 
No links, just copy the parts you need. Chances are you'll customize them and create new ones if you go with this approach.
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralMy vote of 5memberPlutoni_vpt8929 Oct '11 - 7:42 
Very useful. Thanks
QuestionGood OnememberdineshRajan21 Sep '11 - 21:49 
Thanks. It was really helful to me to work on chained null values. Keep up your Great Work.
QuestionMy vote of 5memberFilip D'haene31 Jul '11 - 4:26 
Excellent article!
 
Thanks for sharing. Smile | :)
QuestionA sincere, "Thank you!"memberMythias26 Jul '11 - 20:37 
Dmitri,
 
I'd like to take a moment, as I rarely do, to say thank you for this very beneficial contribution. You're truly a programmer after my own heart. Extension methods are easily one of my favorite constructs in any language. It is not often, that I come across code that changes the way I do something. Well done, Dmitri!
 
Vote: 6 stars => 5 stars (limits of the control /shrug)
Performance as you correctly say is negligible. (and I am a performance freak)
- Mythias (Jason Nickle)

GeneralMy vote of 5memberDrABELL24 Apr '11 - 17:01 
Very elegant solution, well done!
GeneralHopefully a ImprovementmemberMuhammad Shoaib Raja28 Dec '10 - 6:12 
great article man... i was not using extension methods before i came across your article. anyway i just made a small change in your With extension method so that we can avoiding different variation for value types and reference types.
 
  public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
  {
     if (!typeof(TInput).IsValueType && o == null) return default(TResult);
 
     return evaluator(o);
   }

GeneralRe: Hopefully a ImprovementmemberDmitri Nesteruk28 Dec '10 - 9:13 
Err, that's a bit too much Smile | :)
 
What you should have written instead is
if (Equals(o, default(TInput))) return default(TInput);
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralMy vote of 5memberMuhammad Shoaib Raja23 Nov '10 - 4:01 
Great Article that force me to give you 5. Excellent Stuff
GeneralMy vote of 5mvpJosh Fischer20 Oct '10 - 6:07 
Beautiful! I wish I could think of things like this!
GeneralMy vote of 5memberVercas18 Oct '10 - 23:01 
Outstanding article!
Helps me a lot with long chains of classes!
GeneralMy vote of 5memberVinod Kumar Gupta18 Oct '10 - 0:06 
Great stuff. I was looking to do similar but dont have to do now. Thanks dude....
Big Grin | :-D
Cheers
Vinod
GeneralMy vote of 4memberBigdeak11 Oct '10 - 22:32 
This is a really great article! For the nice explaination i would give 5 points.
 
I believe a kind of programming is rather useful for creating small and explicit projects like in VB, this would be very cool as a script-language, simple, but mightful.
If i see code like this without reading the article, i would think it is another language as C#, so i won't understand it at the first view, that may be the problem of each C# developer seeing this code.
 
One small remark:
This kind of programming remember me to the Visual-Basic "with-block".
 
Anyway, good work and keep it up Smile | :)
GeneralMy vote of 5membersashidhar10 Oct '10 - 23:33 
Excellent
GeneralMy vote of 5memberbeatxym10 Oct '10 - 22:03 
It's useful when statements contain "as" in the chain.
GeneralVote 4. Creative (and awesome) but...memberSimon Dufour8 Oct '10 - 7:59 
I voted 4. Your code is impressive, awesome and creative. Seriously.
 
The main problem, I think, is that the drop in cohesion is really drastic. I'm sure that you could have fun using your bit of code but if you work in a team, stuff like that becomes really pricy to maintain. You could basically watch the code for hours and not understand what it does without a proper guide.
 
Anyway. It's interesting and creative. However, I'd discourage using it in a real project to avoid confusion and, instead, promote cohesion.
 
Good work!
GeneralRe: Vote 4. Creative (and awesome) but...memberdakeefer3 Feb '11 - 4:23 
Agreed. It's also not great to debug. Cute, but not easy to maintain on a big project/team.
the cake is a lie.

GeneralReferenceEqualsmemberxanatos8 Oct '10 - 2:11 
Instead of == you should probably use ReferenceEquals([something], null). == can be overloaded and you don't know how it will be overloaded.
GeneralRe: ReferenceEqualsmemberRobert_G1 Dec '10 - 18:51 
Overloaded operators are not applicable within generics.
 
He also used a constraint to only allow reference types, so the null-check should be pretty safe.
GeneralMy vote of 5memberOak Chantosa7 Oct '10 - 14:59 
Very impressive!
GeneralMy vote of 5memberNaveenSoftwares6 Oct '10 - 18:39 
Good work
GeneralMy vote of 5membertomlev5 Oct '10 - 23:15 
Great article, thanks for posting!
GeneralNullable<T> typesmemberILICHPOST5 Oct '10 - 15:15 
Consider to add following extension methods to handle nullable types:
public static TResult? With<TInput, TResult>(this TInput o, Func<TInput, TResult?> evaluator)
            where TInput : class
            where TResult : struct {
    if (o == null) return null;
        return evaluator(o);
}
public static TResult Return<TInput, TResult>(this TInput? o, Func<TInput, TResult> evaluator, TResult failureValue) 
            where TInput : struct {
    if (!o.HasValue) return failureValue;
        return evaluator(o.Value);
}
 
And thanks for good idea !
ILICH

GeneralMy vote of 5memberILICHPOST5 Oct '10 - 14:15 
Fresh idea ! Thanks ! I stuck with constructing dynamic expression - that what is really slow thing
GeneralMy vote of 5memberThomas Eyde3 Oct '10 - 1:41 
Very readable and easy to understand
GeneralMy vote of 5memberMichal Stehlik22 Sep '10 - 20:34 
Very interesting solution and article, but I think this not helps create code better readable (You can also format if into more than one line). Anyway I gave 5 to this article (solution) because it deserves.
GeneralMy vote of 5memberCIDev21 Sep '10 - 5:36 
An interesting way of handling things.
QuestionGreat! and a typo?memberc0ax_lx21 Sep '10 - 4:03 
Hi, Great work! i knew all the countless hours of nullchecking were in vain ;D
a question
 
string postCode = this.With(x => person)
    .If(x => HasMedicalRecord(x))] <-- THAT bracket
    .With(x => x.Address)
    .Do(x => CheckAddress(x))
    .With(x => x.PostCode)
    .Return(x => x.ToString(), "UNKNOWN");
 
is the ] bracket a typo? as you probably have figured out i havent tested the code yet.
A FOO walked into a BAR, and the horse said..

AnswerRe: Great! and a typo?memberDmitri Nesteruk21 Sep '10 - 4:45 
Yep, that's a typo! Thanks for point it out - I have corrected it.
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralMy vote of 5 - one question [modified]memberTylorDurden15 Sep '10 - 21:17 
wow, thats really damn hot Wink | ;) Thats why i love .Net...
 
where you guys "place" this code? iam thinking about to place this in a base class
or a helper class.... what you prefer ?
 
Could anyone please post a little sample, iam not a dot net pro, but i like
the article very much and want to use this stuff. Just a few lines...
 
Maybe iam to sleepy to understand that now.

modified on Thursday, September 16, 2010 5:02 AM

GeneralRe: My vote of 5 - one questionmemberDmitri Nesteruk15 Sep '10 - 23:57 
It doesn't matter where you place the class because these are extension methods. No need to inherit anything Smile | :)
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralRe: My vote of 5 - one questionmemberTylorDurden16 Sep '10 - 0:02 
thanks a lot... now i know that i was sleepy Wink | ;)
GeneralMy vote of 2memberwind surf14 Sep '10 - 4:55 
Suggested method does not make code more readable. Actually, if you're not familiar with the concept the code is less readable.
Not to mention that chain use is the violation of Low of Demeter.
GeneralRe: My vote of 2membersurajfrommumbai8 Oct '10 - 23:12 
I think you wanted reasons to vote 1 for the article.
 
Stupid a****hole
GeneralMy vote of 5memberJon Summers13 Sep '10 - 21:59 
Innovate use of language and imaginative creation of extensions
QuestionA great articlemembernonexisto13 Sep '10 - 20:33 
I see you repost that from GotDotNet. This version is much more easy to read and understand comparing to the original.
 
Yet few things are still waiting for an answer:
What are the performance issues of such approach? How does memory requirement changes? I mean that much lambdas usage isn't it increases the size of assemblies?
And the last which is closer to the obfuscations I presume: how one would debug that thing?
 
I would be grateful if those are added to the article Thumbs Up | :thumbsup:
AnswerRe: A great articlememberDmitri Nesteruk13 Sep '10 - 20:47 
Someone else on GDN did a write-up on this method compared to other methods, including using an Expression for checked member access. Truth be told, I deliberately avoided writing about performance because the kind of performance hit you see here is something you'd get anyway in any location where you use chains of extension methods. (And, needless to say, compared to say reflection, the impact is negligible.)
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralA really cool idea!membertom-englert13 Sep '10 - 19:53 
That's the kind of gimmicks that make fun using .Net
GeneralMy vote of 5membertom-englert13 Sep '10 - 19:49 
Really cool idea
General[My vote of 1] This is not cool.memberjelamid13 Sep '10 - 18:48 
I just read the documentation on the lambda operator, still use mostly C# 2.0, and while this seems to clean up the code visually it slows down the execution.
 
Your first example:
 

string postCode = this.With(x => person)
.With(x => x.Address)
.With(x => x.PostCode);

is used to replace:
 

string postCode = null;
if (person != null && person.Address != null && person.Address.PostCode != null)
{
postCode = person.Address.PostCode.ToString();
}

 
After creating a test program, with person == null your extension will evaluate person == null, null == null, null == null for 3 comparisons. The original if statement will only evaluate person == null for 1 comparison. So for a little visual improvement your execution takes 3 times as long. With the entire chain of objects populated the number of evaluations should be the same.
 
If your code is correct you will see it very few times, if your code is well used it will execute a great many times.
 
This is not cool.
GeneralRe: [My vote of 1] This is not cool.memberDmitri Nesteruk13 Sep '10 - 19:46 
Wahey! A vote of 1! That was bound to happen... and as to your complaints - well, it's obvious to anyone who can read that this way of expressing things implies a performance hit. There's no free lunch here!
 
And actually, it's not '3 times as long'. (I wish.)
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralRe: [My vote of 1] This is not cool.memberOPerttilä14 Sep '10 - 7:42 
I did a test program on this to actually see the difference
 
True: If person is null the difference in execution time is big in relative terms. Depending on what you do with the value it can be more than ten times. I assume it is because in the second (traditional example) the assignment does not take place at all (but did not analyze deeper).
 
However: In absolute terms, the difference in performance is ridiculously low. With one million iterations, the difference is less than 20 milliseconds. I.e. 0.00002 milliseconds per usage of this helper.
 
I would argue that this level of performance hit is an issue only in very rare edge cases - if even there.
 
In any case, even if you disagree, grading a well argumented and written article with "1" is in your words: "Not cool"!
GeneralRe: [My vote of 1] This is not cool.memberDmitri Nesteruk14 Sep '10 - 8:12 
I wouldn't spend so much time answering these posts because, as you can see, they are all single-post temporary accounts registered just to give that 1 or 2 Smile | :)
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralRe: [My vote of 1] This is not cool.memberaman.tur16 Sep '10 - 1:31 
Don't worry about the criticism Dimitri. Though it is good the these users are pointing towards performance penalty so that users who want to use this technique in critical or performance sensitive parts of their code can evaluate on its use. But definitely the article does not deserve vote of 1 or 2 or may be even 3.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 21 Sep 2010
Article Copyright 2010 by Dmitri Nеstеruk
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid