A C# library to write functional code - Part II - Tuples

-

Other posts in the se­ries:

    In F# you usually access them by doing pattern matching, which gives a more intuitive syntax. But again, my C# syntax is not terrible. 
    
    Tuples need to have structural equality, which means that the following has to work:
    
    <pre class="code"><span style="color:rgb(43,145,175);">ArrayList</span> mad1 = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ArrayList</span> { <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">List</span>&lt;<span style="color:rgb(43,145,175);">IEnumerable</span>&lt;<span style="color:rgb(0,0,255);">string</span>&gt;&gt; { <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">string</span>[] { <span style="color:rgb(163,21,21);">"bo"</span> }, <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">string</span>[] { <span style="color:rgb(163,21,21);">"bo"</span> } },<br />                               32, <span style="color:rgb(163,21,21);">"bo"</span>, <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">int</span>[] { 4, 5, 6 } };
    <span style="color:rgb(43,145,175);">ArrayList</span> mad2 = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ArrayList</span> { <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">List</span>&lt;<span style="color:rgb(43,145,175);">IEnumerable</span>&lt;<span style="color:rgb(0,0,255);">string</span>&gt;&gt; { <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">string</span>[] { <span style="color:rgb(163,21,21);">"bo"</span> }, <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">string</span>[] { <span style="color:rgb(163,21,21);">"bo"</span> } },<br />                               32, <span style="color:rgb(163,21,21);">"bo"</span>, <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">int</span>[] { 4, 5, 6 } };
    <span style="color:rgb(43,145,175);">ArrayList</span> mad3 = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ArrayList</span> { <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">List</span>&lt;<span style="color:rgb(43,145,175);">IEnumerable</span>&lt;<span style="color:rgb(0,0,255);">string</span>&gt;&gt; { <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">string</span>[] { <span style="color:rgb(163,21,21);">"bo"</span> }, <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">string</span>[] { <span style="color:rgb(163,21,21);">"bo"</span> } },<br />                               32, <span style="color:rgb(163,21,21);">"bo"</span>, <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(0,0,255);">int</span>[] { 4, 5, 5 } };</pre>
    
    
    
    <pre class="code"><span style="color:rgb(43,145,175);">Assert</span>.AreEqual(<span style="color:rgb(43,145,175);">F</span>.Tuple(mad1, mad2, mad1), <span style="color:rgb(43,145,175);">F</span>.Tuple(mad2, mad1, mad2));
    <span style="color:rgb(43,145,175);">Assert</span>.AreNotEqual(<span style="color:rgb(43,145,175);">F</span>.Tuple(mad1, mad2, mad1), <span style="color:rgb(43,145,175);">F</span>.Tuple(mad1, mad3, mad1));</pre>
    
    You can use Tuples as return values, parameters, locals etc. Unfortunately, the syntax is ugly when Tuples are part of the signature of a function:
    
    <pre class="code"><span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(43,145,175);">Tuple</span>&lt;<span style="color:rgb(0,0,255);">string</span>, <span style="color:rgb(43,145,175);">IEnumerable</span>&lt;<span style="color:rgb(43,145,175);">Tuple</span>&lt;<span style="color:rgb(0,0,255);">string</span>, <span style="color:rgb(43,145,175);">ObservationHistory</span>&gt;&gt;&gt; Execute() {
}</pre>
    
    With the above information, you can be a user of Tuples. From this point on, I'll talk about some details of the implementation (I also attach the full code to this post as a zip file).
    
    <pre class="code"><span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">class</span> <span style="color:rgb(43,145,175);">Tuple</span>&lt;T1&gt; {
    <span style="color:rgb(0,0,255);">public</span> Tuple(T1 t1) {
        Item1 = t1;
    }
    <span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">readonly</span> T1 Item1;

#region Equals, GetHashCode, ==, != } pub­lic class Tuple<T1, T2> : Tuple<T1> { pub­lic Tuple(T1 t1, T2 t2) : base(t1) { Item2 = t2; } pub­lic read­only T2 Item2; #region Equals, GetHashCode, ==, != }

    &nbsp;
    
    So, Tuples are classes, not structs. The reason for it is fully described in <a href="http://blogs.msdn.com/lucabol/archive/2008/01/11/creating-an-immutable-value-object-in-c-part-v-using-a-library.aspx" target="_blank"><strong><font color="#006bad">this series of posts</font></strong></a>. They also inherit from one another. There are pros and cons to that. The main pros are that I had to write less code and that you can pass a Tuple<int, string> when a function expects a Tuple<int, string, int>. The main drawback is that you can pass a Tuple<int, string> when a function expects a Tuple<int, string, int>.&nbsp; Also notice the use of public fields. These&nbsp;is a problem with frameworks that insist on properties (i.e. Data Binding). Also, I just got to 5 as arity goes. The day I need 6 items, I'll add another one. It is boilerplate code (that I'd still like not to write).
    
    The Equals method is a bit convoluted:
    
    <pre class="code"><span style="color:rgb(0,0,255);">internal</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">class</span> <span style="color:rgb(43,145,175);">Utils</span> {
    <span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">void</span> CheckNull&lt;T&gt;(T t) {
        <span style="color:rgb(0,0,255);">if</span> (t == <span style="color:rgb(0,0,255);">null</span>)
            <span style="color:rgb(0,0,255);">throw</span> <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ArgumentNullException</span>();
    }<br />     }</pre>
    
    
    
    <pre class="code"><span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">override</span> <span style="color:rgb(0,0,255);">bool</span> Equals(<span style="color:rgb(0,0,255);">object</span> right) {
        <span style="color:rgb(43,145,175);">Utils</span>.CheckNull(right);
        <span style="color:rgb(0,0,255);">if</span> (<span style="color:rgb(0,0,255);">object</span>.ReferenceEquals(<span style="color:rgb(0,0,255);">this</span>, right))
            <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">true</span>;
        <span style="color:rgb(0,0,255);">if</span> (<span style="color:rgb(0,0,255);">this</span>.GetType() != right.GetType())
            <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">false</span>;
        <span style="color:rgb(0,0,255);">var</span> rightT = right <span style="color:rgb(0,0,255);">as</span> <span style="color:rgb(43,145,175);">Tuple</span>&lt;T1, T2, T3&gt;;
        <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">base</span>.Equals(rightT) && <span style="color:rgb(43,145,175);">F</span>.DSEquals(<span style="color:rgb(0,0,255);">this</span>.Item3, rightT.Item3);
    }</pre>
    
    
    
    I always get complaints when I show Equals methods that throw if null is passed in, but I stand by my logic, that&nbsp;the presence of null for these categories of&nbsp;&#8216;structurally equal' classes is symptom of an error and I want to be notified.&nbsp;Returning false doesn't do that.
    
    <pre class="code"><span style="color:rgb(0,0,255);">internal</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">bool</span> DSEquals&lt;T&gt;(T left, T right) {
        <span style="color:rgb(0,0,255);">if</span> (left == <span style="color:rgb(0,0,255);">null</span> && right == <span style="color:rgb(0,0,255);">null</span>)
            <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">true</span>;
        <span style="color:rgb(0,0,255);">if</span> (left == <span style="color:rgb(0,0,255);">null</span> && right != <span style="color:rgb(0,0,255);">null</span>)
            <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">false</span>;
        <span style="color:rgb(0,0,255);">var</span> len = left <span style="color:rgb(0,0,255);">as</span> <span style="color:rgb(43,145,175);">IEnumerable</span>;
        <span style="color:rgb(0,0,255);">var</span> ren = right <span style="color:rgb(0,0,255);">as</span> <span style="color:rgb(43,145,175);">IEnumerable</span>;
        <span style="color:rgb(0,0,255);">if</span> (len == <span style="color:rgb(0,0,255);">null</span> && ren == <span style="color:rgb(0,0,255);">null</span>)
            <span style="color:rgb(0,0,255);">return</span> left.Equals(right);
        <span style="color:rgb(0,0,255);">if</span> (len == <span style="color:rgb(0,0,255);">null</span> && ren != <span style="color:rgb(0,0,255);">null</span>)
            <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">false</span>;
        <span style="color:rgb(0,0,255);">if</span> (len != <span style="color:rgb(0,0,255);">null</span> && ren == <span style="color:rgb(0,0,255);">null</span>)
            <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">false</span>;
        <span style="color:rgb(0,0,255);">return</span> SequenceEqual(len, ren);
    }</pre>
    
    DSEquals check the content of the Tuple and forward to SequenceEqual in case one slot of the Tuple contains an IEnumerable.
    
    <pre class="code"><span style="color:rgb(0,0,255);">internal</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">bool</span> SequenceEqual(<span style="color:rgb(43,145,175);">IEnumerable</span> en1, <span style="color:rgb(43,145,175);">IEnumerable</span> en2) {
        <span style="color:rgb(0,0,255);">var</span> enumerator = en2.GetEnumerator();
        <span style="color:rgb(0,0,255);">foreach</span> (<span style="color:rgb(0,0,255);">var</span> o <span style="color:rgb(0,0,255);">in</span> en1) {
            <span style="color:rgb(0,0,255);">if</span> (!enumerator.MoveNext())
                <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">false</span>;
            <span style="color:rgb(0,0,255);">if</span> (!DSEquals(o, enumerator.Current))
                <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">false</span>;
        }
    SequenceEqual checks that the number of items in the enumerator is the same and recursively calls DSEqual to check structural equality for items at the same index in the two enumerators.
    
    GetHashCode is trivial (and maybe trivially wrong, one of these days I'll learn everything about GetHashCode() ).
    
    <pre class="code"><span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">override</span> <span style="color:rgb(0,0,255);">int</span> GetHashCode() {
        <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">base</span>.GetHashCode() | Item3.GetHashCode();
    }</pre>
    
    
    
    The equality operators are equally simple.
    
    <pre class="code"><span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">bool</span> <span style="color:rgb(0,0,255);">operator</span> ==(<span style="color:rgb(43,145,175);">Tuple</span>&lt;T1, T2, T3&gt; t1, <span style="color:rgb(43,145,175);">Tuple</span>&lt;T1, T2, T3&gt; t2) {
        <span style="color:rgb(43,145,175);">Utils</span>.CheckNull(t1);
        <span style="color:rgb(43,145,175);">Utils</span>.CheckNull(t2);
        <span style="color:rgb(0,0,255);">return</span> t1.Equals(t2);
    }
    <span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">bool</span> <span style="color:rgb(0,0,255);">operator</span> !=(<span style="color:rgb(43,145,175);">Tuple</span>&lt;T1, T2, T3&gt; t1, <span style="color:rgb(43,145,175);">Tuple</span>&lt;T1, T2, T3&gt; t2) {
        <span style="color:rgb(0,0,255);">return</span> !(t1 == t2);
    }</pre>
    
    And ToString() prints my favorite Tuple format.
    
    <pre class="code"><span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">override</span> <span style="color:rgb(0,0,255);">string</span> ToString() {
        <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">string</span>.Format(<span style="color:rgb(163,21,21);">"{0},{1}"</span>, <span style="color:rgb(0,0,255);">base</span>.ToString(), Item3.ToString());
    }</pre>
    
    I'm sure you can find plenty of issues in this code. As always, it is not &#8216;production ready', it is more &#8216;Luca having fun doing it'. In any case, there are some testcases in the solution to check the extent of my testing.
    
    In the next post we'll look at Records.
    
    [Functional.zip](https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/08/36/99/78/Functional.zip)

Tags

16 Comments

Comments

What about:
in­ter­nal sta­tic bool DSEquals<T>(T left, T right) {
   if (left == null && right == null)
       return true;
   if (left == null && right != null)
       return false;
   var len = left as IEnumerable;
   var ren = right as IEnumerable;
   if (len == null && ren == null)
       return left.Equals(right);
   if (len != null && ren != null)
       return SequenceEqual(len, ren);
   return false;
}

Adam Cooper

2008-04-16T09:47:44Z

Luca,
Thanks so much for shar­ing this, both your posts and the source code.
I re­ally like your idea of bring­ing some (more) of F#’s good­ness to C#. I spent some time in­ves­ti­gat­ing if F# was the right tool for some of our com­pany pro­jects and, while I think F# is fan­tas­tic, our do­main is one of stor­ing and re­triev­ing lots of hi­er­ar­chi­cal data, a do­main which is ideal for an ob­ject-ori­ented lan­guage like C#. So C# is the clear choice for us (in the vast ma­jor­ity of cases). However, there are quite a few as­pects of func­tional pro­gram­ming I’m re­ally be­gin­ning to ad­mire.
I’ve never been happy with the OO par­a­digm of many-in­puts-one-out­put, and I find your Tuples so­lu­tion ex­cit­ing. Can you com­ment on how your Tuples im­ple­men­ta­tion dif­fers from anony­mous types in C#?
Thanks and keep up the good work,
Adam Cooper

Thanks for the kind words.
There are two dif­fer­ences:
1. Anonymous types al­low you to give a name to each prop­erty, while tu­ple don’t.
2. You can­not re­turn anony­mous types from meth­ods or pass­ing them as pa­ra­me­ters. So their used is lim­ited to the body of a method.
Overall anony­mous types are a mix of tu­ples and records. I’ll talk about records in an up­com­ing post.

Luca Bolognese's WebLog

2008-04-21T13:35:42Z

Previous posts: Part I - Background Part II - Tuples Now that we know what Tuples are, we can start talk­ing

Charlie Calvert's Community Bl

2008-04-23T16:58:14Z

Welcome to the forty-third is­sue of Community Convergence. The last few weeks have been con­sumed by the

Configurator

2008-04-24T04:12:14Z

I’m won­der­ing: Doesn’t CheckNull cause an in­fi­nite loop?
Usually when check­ing null, I would use ReferenceEquals(t, null) be­cause == would call the type’s op­er­a­tor ==, which checks for null caus­ing an in­fi­nite loop.
If CheckNull ac­cepted an ob­ject and was­n’t generic, if would use ob­jec­t’s ==, which works for null. But since it’s generic it should use the == that you sup­plied.
Am I miss­ing some­thing here?

Eamon Nerbonne

2008-04-24T05:04:04Z

I’ve also needed to build these things, and have also run into some of the same is­sues you seem to have here - like the sur­pris­ing un­handi­ness of structs (and even more sur­pris­ing slow­ness).
Incidentally, your hash-code is no good.  You don’t want to use a sym­met­ric com­bi­na­tion of two hash codes, since that means that F.Tuple(true,false) is equal to F.Tuple(false, true) in terms of hash­code.  A non-sym­met­ric com­bi­na­tion is prob­a­bly best; but the in­stantly ob­vi­ous sub­trac­tion is def­i­nitely not a good idea, since that means that all tu­ples of two el­e­ments with iden­ti­cal mem­bers have a hash-code of 0.
I choose to scale the hash­code with a prime num­ber.  Trial and er­ror on my test-set (consisting of lower-case strings which may not be rep­re­sen­ta­tive for oth­ers) found that 137 is fine; so that’s what I use, but I haven’t re­searched the is­sue.
Further, if your tu­ples will be used ex­ten­sively in things like lookup ta­bles, then hash-code cal­cu­la­tion be­comes rel­e­vant:  you might want to store the hash­code once and reuse it (which can also help speed up equal­ity com­par­isons when these are per­formed out­side of a hash table).

It does­n’t go into an in­fi­nite loop be­cause it is generic. We can­not call the over­load == be­cause at that point in the code we don’t know what it is. So we box it and compare the point­er’ in­sted. The com­mented IL for the func­tion should clar­ify:
.method pub­lic hide­bysig sta­tic void CheckNull<T>(!!T t) cil man­aged
{
   .maxstack 2
   .locals init (
       [0] bool CS40000)
   L_0000: nop
   L_0001: ldarg.0
   L_0002: box !!T // We box it
   L_0007: ld­null
   L_0008: ceq // We com­pare it
   L_000a: ldc.i4.0
   L_000b: ceq
   L_000d: st­loc.0
   L_000e: ld­loc.0
   L_000f: brtrue.s L_0017
   L_0011: newobj in­stance void [mscorlib]System.ArgumentNullException::.ctor()
   L_0016: throw
   L_0017: ret
}

Eamon: I know my hash is bad. I was too lazy to pro­duce a bet­ter one :)

David Nelson

2008-04-28T00:42:14Z

The prob­lem with your this should never be null” logic is that it pre­vents the user from ever writ­ing code like this:
Tuple<int, int> MyMethod()
{
  Tuple<int, int> re­sult = null;
  // Some code which may or may not set re­sult
  if(re­sult == null)
  {
     // Some other code which al­ways sets the re­sult
  }
  re­turn re­sult;
}
This is a rea­son­ably com­mon pat­tern. Your code is check­ing for null in the wrong place. It is not un­rea­son­able that any Tuple ref­er­ence might  sometimes be null. Tuples should only be checked for null where it is sure that they should never be null.

Hi David,
I used this code to learn func­tional pro­gram­ming. In that style vari­ables are im­mutable, which makes the code you pre­sent in­valid. So, in my sce­nario, my de­ci­sion makes sense.
In a more generic sense, you have to trade off the an­noy­ance of not be­ing able to write the code you pre­sent and the an­noy­ance of not catch­ing bugs be­cause you re­turn false in­stead of throw­ing. Reasonable peo­ple can come up in dif­fer­ent places in that de­bate.

John Mertus

2008-05-03T21:56:50Z

Why not just re­turn a dic­tio­nary from the func­tion.  The key of the dic­tio­nary are the names of the el­e­ments of the class, the value ob­jects.  For ex­am­ple
 var Person = GetPerson(<some id>);
 int age = Person[“age”]).ToInt32()
 string name = Person[“FirstName”].ToString();
(Here .ToInt32 is the damn ex­ten­sion C# should have for ob­jects, why the hell does­n’t it)
Of course  
 The big prob­lem is the data bind­ing be­comes a var in the java sense; that it is like a late bind­ing and fails at run­time not com­pile time.   But it gives a bet­ter syn­tax and is more read­able than tu­ples.

David Nelson

2008-05-04T18:47:21Z

@John
Here .ToInt32 is the damn ex­ten­sion C# should have for ob­jects, why the hell does­n’t it”
Because the vast ma­jor­ity of types can­not be rea­son­ably rep­re­sented by an Int32. Presumably if such a ToInt32 method ex­isted, these types would all have to throw an ex­cep­tion. Why would you want to add a pars­ing method to every sin­gle ob­ject which will fail most of the time?

adamjcooper.com/blog

2008-06-03T16:59:59Z

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

Luca Bolognese's WebLog

2008-07-15T05:46:24Z

Other posts in the se­ries: Part I - Background Part II - Tuples Part III - Records Part IV - Type Unions