A C# library to write functional code - Part IV - Type Unions
Luca Bolognese -Other posts in the series:
- Part I - Background
-
- Part III - Records
- Part IV - Type Unions{.}
- Part V - The Match operator{.} I’m sorry for my prolonged absence in the middle of this series of posts. I’m on a long paternity leave in Italy (playing beach volley every day). It’s hard to have the discipline
A bunch of you wrote telling me to finish this. So here I go: let’s talk about type unions. First of all: they are not called like that. The correct name is discriminated unions. I have no idea why I call them differently, but I want to be consistent with my previous mistake.
For those of you with a C++ background (like myself) they are like unions, just better (or worse depending on your convictions). They let you define a type that can represent one of several different types. You can then use the ‘match’ operator (discussed in the next post) to pattern match against it.
I won’t elaborate on the pros and cons of this style of programming versus using polymorphism. I just want to show you how I implemented this construct in C#. As always, my usual caveat: this is just ‘educational code’, use it at your own risk, no extensive or perf related test has been done on it. You can download the zip file and check my unit tests for yourself.
How type unions are used
In my world, you declare a type union like this:
public class Person { }
-
public class Dog { } public class Friend : TypeUnion<Person, Dog> { public Friend(Person p) : base§ { } public Friend(Dog d) : base(d) { } }
You inherit a type union from the TypeUnion class and use generic parameters that correspond to the types that the union can represent.
You can then create a type union as:
<pre class="code"><span style="color:blue;">var </span>fr = <span style="color:blue;">new </span><span style="color:#2b91af;">Friend</span>(<span style="color:blue;">new </span><span style="color:#2b91af;">Dog</span>());</pre>
Test its type by:
<pre class="code"><span style="color:#2b91af;">Assert</span>.IsTrue(fr.Is<<span style="color:#2b91af;">Dog</span>>());
Assert.IsFalse(fr.Is<Person>());
Cast it to one of the types they represent:
<pre class="code"><span style="color:blue;">var </span>d = fr.As<<span style="color:#2b91af;">Dog</span>>();</pre>
Or use it with the ‘match' operator (fully explained in an upcoming post):
<pre class="code"><span style="color:blue;">var </span>r = <span style="color:#2b91af;">F</span>.Match(fr,
f => f.Is<<span style="color:#2b91af;">Dog</span>>(), f => f.As<<span style="color:#2b91af;">Dog</span>>().ToString(),
f => f.Is<<span style="color:#2b91af;">Person</span>>(), f => f.As<<span style="color:#2b91af;">Person</span>>().ToString());
Assert.AreEqual(r, new Dog().ToString());
Or the slightly more pleasing:
<pre class="code">r = <span style="color:#2b91af;">F</span>.Match(fr,
(<span style="color:#2b91af;">Person </span>p) => p.ToString(),
(<span style="color:#2b91af;">Dog </span>d) => d.ToString());
Assert.AreEqual(r, new Dog().ToString());
You get the idea.
**How they are implemented**
Nothing really sophisticated going on here. Let's take as an example a type union that can represent two types. I have versions that go to 5 types in the zip file.
First of all a TypeUnion is a Record:
<pre class="code"><span style="color:blue;">public class </span><span style="color:#2b91af;">TypeUnion</span><T1, T2> : <span style="color:#2b91af;">Record</span><T1, T2> {</pre>
It has overloaded constructors to create a type union of a particular type:
<pre class="code"><span style="color:blue;">public </span>TypeUnion(T1 t1)
: <span style="color:blue;">base</span>(t1, <span style="color:blue;">default</span>(T2)) {
UnionType = t1.GetType();
} public TypeUnion(T2 t2) : base(default(T1), t2) { UnionType = t2.GetType(); }
UnionType is used to ‘remember' which type it is:
<pre class="code"><span style="color:blue;">protected </span><span style="color:#2b91af;">Type </span>UnionType;</pre>
It also has properties to return the objects of all the types that can be stored:
<pre class="code"><span style="color:blue;">protected </span>T1 Type1 { <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>state.Item1; } }
protected T2 Type2 { get { return state.Item2; } }
The ‘Is' operator is simply implemented as:
<pre class="code"><span style="color:blue;">public bool </span>Is<K>() {
<span style="color:blue;">return typeof</span>(K).IsAssignableFrom(UnionType);
}
And the ‘As' operator looks like so:
<pre class="code"><span style="color:blue;">public </span>K As<K>() {
<span style="color:blue;">if </span>(!Is<K>())
<span style="color:blue;">throw new </span><span style="color:#2b91af;">Exception</span>(<span style="color:blue;">string</span>.Format(<br /> <span style="color:#a31515;">"In a TypeUnion cannot cast from {0} to {1}"</span>,<br /> UnionType.Name, <span style="color:blue;">typeof</span>(K).Name));
<span style="color:blue;">if </span>(<span style="color:blue;">typeof</span>(T1) == UnionType)
<span style="color:blue;">return </span>(K)(<span style="color:blue;">object</span>) Type1;
<span style="color:blue;">if </span>(<span style="color:blue;">typeof</span>(T2) == UnionType)
<span style="color:blue;">return </span>(K)(<span style="color:blue;">object</span>) Type2;
<span style="color:blue;">throw new </span><span style="color:#2b91af;">Exception</span>(<span style="color:#a31515;">"Shouldn't get here"</span>);
}
I leave as an exercise to the reader to understand what happens if T1 and T2 are the same type or inherit from the same type. I could have written code to handle this case in a more explicit manner, but didn't.
Also, by reviewing my code I found an obvious bug in my Is<K>/As<K> code. I fixed it and re-posted the zip file in the second post of this series.
Now back to the beach. Next post is on the ‘match' operator.
Tags
- CSHARP
10 Comments
Comments
Luca Bolognese's WebLog : A C#
2008-06-06T04:49:52ZPingBack from http://blogs.msdn.com/lucabol/archive/2008/04/01/a-c-library-to-write-functional-code-part-i-background.aspx
jaredpar
2008-06-06T11:42:34ZI’m curious as why you chose to inherit from Record as opposed to using it as a member variable. From your example it doesn’t look like a TypeUnion “is” a Record so much as it uses a Record.
lucabol
2008-06-09T07:57:58ZI cannot recall the reason, if there ever was one.
On the face of it, you are right.
configurator
2008-06-09T18:35:32ZTo access the type union you use the Is and As operator. That means that the usage of Type1 and Type2 in the record is not needed.
You can simply keep the value in a member object-type field. Then, you would simplify the code of As<> and not really much change anything else - except the constantly unused memory slot.
Also, you would be more efficient because the cast to object would be unnecessary. The only two things that would enforce or use the given types would be the constructor - which would only use the given types - and the match operator.
Just my two pence.
santhiran
2008-06-11T08:52:53Zhi
very nice your teachinfg methosd pls sent all C# programming learn tools
Thank You
Coder
2008-06-17T22:21:07ZHow can it be useful?! Does anybody have a real-world example?
Strange Cat
2008-06-25T10:36:42ZGood to know lots of italian work for Microsoft :)
Ciao
Hasan Naqvi
2008-06-27T08:15:18Znice but wud u please tell us the usage of type unions in real world scenerios.
Humberto Moreno
2008-06-30T11:00:46ZI think it would be a good idea to extend the base class to support not just two types, but multiple types. - Btw. I agree with Jared Parsons suggestion.
Overall it’s a good idea. Thanks
Luca Bolognese's WebLog
2008-07-15T05:46:25ZOther posts in the series: Part I - Background Part II - Tuples Part III - Records Part IV - Type Unions