Functional Inspiration – Irrelevantable, part 1

Trying to express something more than Nullable<T>

This is the second entry of a blog series about how functional programming, and the F# training I recently went to, gives me inspiration in my day-to-day work in C#. This post will show how you can build an Irrelevantable<T> generic type in C#, and how you could do the same in F#.

I had recently to cope with a situation where a value, let’s say a price for instance, could have 3 different states :

  1. It could simply be a valid known price,
  2. It could be absent,
  3. It could be irrelevant : for a given situation, that price could have neither a value nor none, this price has simply no possible meaning.

To handle the present / absent cases, if the value is a reference type then the null value is sufficient, if it is a value type we can use a Nullable<T>. But how do you store the “relevant / irrelevant” status ? As I had to handle that for several properties, I wanted to build a structured object that I could reuse. As I had several different types of data, I also wanted it to be generic. My answer was simply to build an Irrelevantable<T> structure with the same behavior as the Nullable<T> structure.

public struct Irrelevantable<T>

As for Nullable<T>, a structure is appropriate, because it will be copied by value, and not by reference. Then comes the members, properties and the constructor :

private bool isRelevant;
public bool IsRelevant { get { return isRelevant; } }

private T value;
public T Value
{
    get
    {
        if (!this.IsRelevant)
            throw new InvalidOperationException(
                "This Irrelevantable<T> instance" +
                "is irrelevent.");
        return this.value;
    }
}

public Irrelevantable(bool isRelevant, T value)
{
    this.isRelevant = isRelevant;
    this.value = value;
}

The next step was to override the base methods for object comparison :

public override int GetHashCode()
{
    return this.IsRelevant
        ? this.value.GetHashCode()
        : -1;
}

public override bool Equals(object other)
{
    if (!this.IsRelevant)
    {
        if (other is Irrelevantable<T>)
        {
            return !((Irrelevantable<T>)other).IsRelevant;
        }

        return false;
    }
    else
    {
        if (other is Irrelevantable<T>)
        {
            return this.value.Equals(
                ((Irrelevantable<T>)other).value);
        }

        return this.value.Equals(other);
    }
}

This way, two Irrelevant objects are equal if they have the same type, and two relevant objects are equal if they hold equal values.

Next, we define implicit and explicit cast operators in order to ease the use of the type :

public static implicit operator Irrelevantable<T>(T value)
{
    return new Irrelevantable<T>(true, value);
}

public static explicit operator T(Irrelevantable<T> value)
{
    return value.Value;
}

Finally, we add a static property to represent the Irrelevant value :

public static Irrelevantable<T> Irrelevant
{
    get
    {
        return new Irrelevantable<T>(false, default(T));
    }
}

The code can now be used to write such expressions :

Irrelevantable<string> someString = "Some data";
Irrelevantable<string> irrelevantString =
    Irrelevantable<string>.Irrelevant;

Irrelevantable<int?> noInt = null;
Irrelevantable<int?> someInt = 10;
Irrelevantable<int?> irrelevantInt =
    Irrelevantable<int?>.Irrelevant;

As I chose to make the cast from the standard type to Irrelevantable implicit, and the opposite cast explicit (as the Nullable type does), we have the same feeling casting from and to Nullable, Irrelevantable and object :

object obj = 1;
int? nullable = 2;
Irrelevantable<int> irrelevantable = 3;

int fromObj = (int)obj;
int fromNullable = (int)nullable;
int fromIrrelevantable = (int)irrelevantable;

To be honest, the C# code sample upon which this post is written had been coded before I could do the same in F#. But when I saw this code again a couple days ago I had this immediate thought : “That would be ways simpler in F# !”

The F# implementation a the same functionality will be covered in the next post, but don’t expect it to be very long !

This entry was posted in Functional Inspiration and tagged , . Bookmark the permalink.

Comments are closed.