A C# library to write functional code – Part III – Records


Other posts in the series:


  • Part I – Background
  • Part II – Tuples
  • Part III – Records

  • Part IV – Type Unions

  • Part V – The Match operator

  • Now that we know what Tuples are, we can start talking about Record, as they use a derivative of Tuples under the cover. But first, what is a record?


    Well, in C# parlance a Record is a sort of immutable value object. I talked at length about these fellows in this series of blog posts. In functional parlance, a Record is a Tuple that lets you access its items by name.


    You code Records like this:

        public class Order: Record<string, int> {

    public Order(string item, int qty): base(item,qty) {}

    public string Item { get { return state.Item1;}}
    public int Quantity { get { return state.Item2; } }
    }

    public class Customer: Record<string, IEnumerable<Order>> {

    public Customer(string name, IEnumerable<Order> orders) : base(name, orders) { }

    public string Name { get { return state.Item1; } }
    public IEnumerable<Order> Orders { get { return state.Item2; } }
    }


    You need to do three things:



    1. Inherit from a generic Record class specifying the types of the properties as parameters

    2. Add a constructor that calls back to the Record constructor

    3. Add getters to retrieve the values of the properties (no setters as it is immutable)

    This may seem like plenty of work, but in return you get structural equality. Coding that by hand every time you use a record would be a royal pain. You lose control of the base class, but that is often not a problem as in functional-land you more often use type unions than inheritance.


    How is it implemented?


    First of all it is an abstract class with as many generic parameters as properties that you need in your Record. Let’s use two as an example.

        public abstract class Record<T1, T2> {

    This abstract class has a field of type STuple:

            protected readonly STuple<T1, T2> state;

    What is a STuple? Well it is exactly the same as the Tuple described in Part II, but coded as a struct instead of a class. The reason to use a struct is to not allocate an additional object on the stack. This allows this solution to be as ‘performant’ as simply having coded the fields on the class itself. Or at least I think so …


    The Record class also has a constructor that simply initialize the STuple:

            public Record(T1 t1, T2 t2) { state = F.STuple(t1, t2); }
    where
            internal static STuple<T1, T2> STuple<T1, T2>(T1 t1, T2 t2) {

    return new STuple<T1, T2>(t1, t2);
    }


    The Equals method is very much the same as the Tuple’s one, just delegating to the same DSEqual function that checks equality for Tuples.

            public override bool Equals(object right) {

    Utils.CheckNull(right);

    if (object.ReferenceEquals(this, right))
    return true;

    if (this.GetType() != right.GetType())
    return false;

    var rightT = right as Record<T1, T2>;

    return F.DSEquals(this.state, rightT.state);
    }

    That’s it. Not too difficult as most of the implementation is based on the Tuple’s code. Next post will hopefully be more interesting. It is about Type Unions.

    Comments (13)

    1. gOODiDEA.NET says:

      .NET RestLess – A Simple REST Framework ASP.NET AJAX Overview And Technical Tips A C# library to write

    2. gOODiDEA says:

      .NETRestLess-ASimpleRESTFrameworkASP.NETAJAXOverviewAndTechnicalTipsAC#libraryto…

    3. Welcome to the forty-third issue of Community Convergence. The last few weeks have been consumed by the

    4. By the way, there’s another reason your hash-code algorithm is unwise: Assuming the component hash-codes are uniformly distributed over the integers from 0 to 2^32-1 (thus interpreting the signed integers as unsigned integers here, for simplicity), your will not be:  a binary or never lowers the number of "on" bits; each bit of your result hash code has only a 25% chance to be off – which lowers the entropy of the overall hash code to be around 26 bits, as opposed to 32 bits.

      Finally, tuples of tuples will hash particularly badly; and so will tuples with higher than 2-arity.

    5. lucabol says:

      Eamon: you are right. It is bad. As I said before, I was lazy.

    6. ktmt says:

      Wow, a little more and we’ll finally have the easy-to-use record construct of Pascal in the 1980s.  Just declare a Record type and you’re set.  I have (seriously) often wished for this in C# instead of cumbersome, separate struct or class def’s.

    7. TimNew says:

      This’s a really excellent introduction. I think I begin to know what the functional code is after reading your posts and code. But I have some questions about this:

      For the GetHashCode(), why don’t you try "xor" other than "or"? I think "xor" will get a better job than the "or"~

      For something about all the code, and I found TypeUnion<T> is really useless! Since I’ll never have the change to use a member union!

    8. pligg.com says:

      Previous posts: Part I – Background Part II – Tuples Now that we know what Tuples are, we can start talking about Record, as they use a derivative of Tuples under the cover. But first, what is a record? Well, in C# parlance a Record is a sort of immutabl

    9. The Quest for Quick-and-Easy Class-Based Immutable Value Objects in C# – Part 1: Introduction

    10. The Quest for Quick-and-Easy Immutable Value Objects in C#

    11. Other posts in the series: Part I – Background Part II – Tuples Part III – Records Part IV – Type Unions

    Skip to main content