boustrophedonic

Thoughts on software and architecture

Union Types in C#

This post shows how to easily implement states in C# using a T4 template.

Background

I often run into situations where I need an object that has a number of alternate “states” or “choices”. The set of states must be treated as “one thing” for general manipulation, but on the other hand, each state will often have slightly different behavior from the other states.

Some examples:

  • A shopping cart might have states Empty, Active and Paid.
    • You can call “AddItem” but not “RemoveItem” on the Empty state
    • You can call “AddItem” and “RemoveItem” and also “Pay” on the Active state
    • You can’t do anything with the Paid state. Once paid, the cart is immutable.
  • A domain object such as Product might have states Valid and Invalid.
    • Only Valid objects can be saved to disk.
  • A game such as chess might have states WhiteToPlay, BlackToPlay and GameOver.
    • Only the GameOver state has a “Winner” method.
    • Only the other states have a “Play” method.

So given this common situation, what kinds of ways are there to implement these kinds of cases?

The inheritance based approach

The standard approach in a object-oriented language is to use inheritance. There is a class for each state, and each state inherits from a common base class or interface.

But where should the custom behavior live?

For example, in the shopping cart example, should the “RemoveItem” method be available at the interface level?

Approach 1: Define all possible actions at the interface level

Let’s say the “RemoveItem” method is available at the interface level, then the interface would look like this:

1
2
3
4
5
6
interface ICartState
{
    ICartState AddItem(Product product);
    ICartState RemoveItem(Product product);
    ICartState Pay(decimal amount);
}

Cart State - Approach 1

But what should the empty state implementation do with the methods that are not relevant? It can either throw an exception or ignore them, as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CartStateEmpty : ICartState
{
    public ICartState AddItem(Product product)
    {
        // implementation
    }

    public ICartState RemoveItem(Product product)
    {
        // throw not implemented exception or ignore
    }

    public ICartState Pay(decimal amount)
    {
        // throw not implemented exception or ignore
    }
}

Throwing an exception seems a bit drastic, and ignoring the call seems even worse. Is there another approach?

Approach 2: Define actions only at the appropriate level

The next approach is that the base interface should only have genuinely common code, and each subclass implements its own special behavior.

Cart State - Approach 2

So the code for the shopping cart would look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface ICartState
{
    // nothing in common between the states
}

class CartStateEmpty : ICartState
{
    public ICartState AddItem(Product product)
    {
        // implementation
    }
}

class CartStateActive : ICartState
{
    public ICartState AddItem(Product product) {} // implementation

    public ICartState RemoveItem(Product product) {} // implementation

    public ICartState  Pay(decimal amount) {} // implementation 
}

class CartStatePaid : ICartState
{
    public decimal GetAmountPaid() {} // implementation 
}

This is much cleaner, but now the question is: how does the client know which state the cart is in?

Typically, the caller would have to try downcasting the object to each state in turn. The client code would look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private class CientWithDowncasting
{
    public ICartState AddProduct(ICartState currentState, Product product)
    {
        var cartStateEmpty = currentState as CartStateEmpty; //CAST!
        if (cartStateEmpty != null)
        {
            return cartStateEmpty.AddItem(Product.ProductY);
        }

        var cartStateActive = currentState as CartStateActive; //CAST!
        if (cartStateActive != null)
        {
            return cartStateActive.AddItem(Product.ProductY);
        }

        // paid state -- do nothing    
        return currentState;
    }
}

But this approach is not only ugly, it’s all error prone, as the client has to do all the work.

For example, what happens if the client forgets to cast properly? And what happens if the requirements change and there are now four states to handle? These kinds of errors will be hard to catch.

Approach 3: The double dispatch or visitor pattern

In OO design, a reliance on this kind of branching and casting for control flow is always a sign that polymorphism is not being used properly. And indeed there is a way to avoid downcasting by using so-called “double dispatch” or its big brother, the visitor pattern.

The idea behind it is that the caller does not know the type of the state, but the state knows its own type, and can call back a type-specific method on the caller. For example, the empty state can call a “VisitEmpty” method, while the active state can call a “VisitActive” method, and so on. In this way, no casting is used. Furthermore, if the number of states increase, the code will break until a handler for the new state is implemented.

To use this approach, first we implement a visitor interface as follows:

1
2
3
4
5
6
interface ICartStateVisitor
{
    ICartState VisitEmpty(CartStateEmpty empty);
    ICartState VisitActive(CartStateActive active);
    ICartState VisitPaid(CartStatePaid paid);
}

Next we change the base interface to allow the visitor to call on the state:

1
2
3
4
interface ICartState
{
    ICartState Accept(ICartStateVisitor visitor);
}

Finally, for each state, we implement the appropriate VisitXXX method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class CartStateEmpty : ICartState
{
    public ICartState Accept(ICartStateVisitor visitor)
    {
        return visitor.VisitEmpty(this);
    }
}

class CartStateActive : ICartState
{
    public ICartState Accept(ICartStateVisitor visitor)
    {
        return visitor.VisitActive(this);
    }

class CartStatePaid : ICartState
{
    public ICartState Accept(ICartStateVisitor visitor)
    {
        return visitor.VisitPaid(this);
    }
}

Now we are ready to use it – and here is where our problems start! Because, for each different set of visitor behavior, we have to implement a custom class.

For example, when we want to add an item, we have to create an instance of AddProductVisitor and when we want to pay for something, we need to create a PayForCartVisitor.

Here’s some code to show what I mean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class CientWithVisitor
{
    class AddProductVisitor: ICartStateVisitor
    {
        public Product productToAdd;
        public ICartState VisitEmpty(CartStateEmpty empty) { empty.AddItem(productToAdd); return empty; }
        public ICartState VisitActive(CartStateActive active) { active.AddItem(productToAdd); return active; }
        public ICartState VisitPaid(CartStatePaid paid) { return paid; }
    }

    class PayForCartVisitor : ICartStateVisitor
    {
        public decimal amountToPay;
        public ICartState VisitEmpty(CartStateEmpty empty) { return empty; }
        public ICartState VisitActive(CartStateActive active) { active.Pay(amountToPay); return active; }
        public ICartState VisitPaid(CartStatePaid paid) { return paid; }
    }

    public ICartState AddProduct(ICartState currentState, Product product)
    {
        var visitor = new AddProductVisitor() { productToAdd = product };
        return currentState.Accept(visitor);
    }

    public ICartState Pay(ICartState currentState, decimal amountToPay)
    {
        var visitor = new PayForCartVisitor() { amountToPay = amountToPay };
        return currentState.Accept(visitor);
    }
}

Phew! It does work, and it is polymorphic and type safe. But it seems like an awful lot of scaffolding is needed just to do something straightforward. An interface, two new classes, and nine new methods!

Here’s are all the players visualized as a diagram:

Cart State - Approach 3

Compared with the second approach, you can see that it is a lot more complicated. And this is a simple example! It reminds me of the ”Kingdom of Nouns”.

Surely there must be another way – combining the simplicity of the second approach with the safeness of the visitor approach?

Algebraic data types and “Sum” types

Let’s step back and see how other languages do it. In some languages, especially functional languages such as Haskell and F#, the type system is not based around classes, but instead is based around mathematics.

The idea is that given a number of smaller types, you can combine them in various ways to make larger, composite types. There are two ways of creating these combinations, “addition” and “multiplication”, and the kinds of types built this way are called “algebraic data types”.

Multiplication of types means taking every possible value of one type and combining it with every possible value of another type. You might already be familiar with this from SQL, because it is similar to a CROSS JOIN. The resulting composite types are called “product” types.

For example a Date type might be created by combining every possible Day value with every possible Month value with every possible Year value . So the type is written Date type = Day type x Month type x Year type.

On the other hand, addition of types means having a set of types and choosing only one value at a time from the union of all of possible values.

For example, we could define a new Temperature type as EITHER one value from the set of temperatures in DegreesF OR one value from the set of temperatures in DegreesC. The resulting composite types are called “sum” or “union” types. So the temperature type is written Temperature type = DegreesF type + DegreesC type.

What does that mean in practice? It means that “sum” types are perfect for modelling situations where there are mutually exclusive states or choices.

In F#, for example, we can create types for each shopping cart state, and then combine (“sum”) them into a composite type. Here’s the F# code:

1
2
3
4
5
6
7
8
9
10
// three different types, each representing a state
type CartStateEmpty = NoItems
type CartStateActive = { UnpaidItems:Product list; }
type CartStatePaid = { PaidItems:Product list; AmountPaid:decimal}

// then create a new type that combines them all as distinct choices 
type CartState =
    | Empty of CartStateEmpty
    | Active of CartStateActive
    | Paid of CartStatePaid

In F#, a CartState must be one of the possible choices. That is, it can be Empty or Active or Paid, but only one at any one time.

And because of this, it is simple to write code that depends on the particular choice. For example, to add a product to the cart, the client code would look something like this:

1
2
3
4
5
let addProductFromCart cart product =
   match cart with
   | Empty state -> state.AddItem product
   | Active state -> state.AddItem product
   | Paid state -> cart // do nothing

You can think of match cart with code as a kind of switch statement that has a different case for each state. Each case is a sort of lambda expression, which in C# would look something like:

1
2
3
(CartStateEmpty state) => state.AddItem(product)
(CartStateActive state) => state.AddItem(product)
(CartStatePaid state) => // do nothing

The particular state that the object is in determines which lambda expression to execute.

Here is the similar code for removing a product from the cart:

1
2
3
4
5
6
7
8
9
10
let removeProductFromCart cart item =
   match cart with
   | Empty state ->
      printfn "ERROR: The cart is empty"
      cart   // return the cart 
   | Active state ->
      state.Remove item
   | Paid state ->
      printfn "ERROR: The cart is paid for"
      cart   // return the cart

and so on.

For more details on how F# does this, see my post on the F# implementation of a shopping cart.

Implementing choices in C#

The F# code gives us a clue on how we might solve the problem in C#.

The idea is that for each possible state, we pass in a lambda specifically designed for that state. We do this through a single method call. In other words we provide a list of functions but we don’t know which one will actually get evaluated – we let the cart decide, based on its state.

Here is some example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
partial interface ICartState
{
  ICartState Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        );
}
  
class CartStateEmpty : ICartState
{
  ICartState ICartState.Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        )
  {
        // I'm the empty state, so invoke cartStateEmpty 
      return cartStateEmpty(this);
  }
}
  
class CartStateActive : ICartState
{
  ICartState ICartState.Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        )
  {
        // I'm the active state, so invoke cartStateActive
      return cartStateActive(this);
  }
}
  
class CartStatePaid : ICartState
{
  ICartState ICartState.Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        )
  {
        // I'm the paid state, so invoke cartStatePaid
      return cartStatePaid(this);
  }
}

And here is an example of how a client might call it in practice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public ICartState AddProduct(ICartState currentState, Product product)
{
    return currentState.Transition(
        cartStateEmpty => cartStateEmpty.AddItem(product),
        cartStateActive => cartStateActive.AddItem(product),
        cartStatePaid => cartStatePaid
        );

}

public void Example()
{
    var currentState = new CartStateEmpty() as ICartState;

    //add some products 
    currentState = AddProduct(currentState, Product.ProductX);
    currentState = AddProduct(currentState, Product.ProductY);

    //pay 
    const decimal paidAmount = 12.34m;
    currentState = currentState.Transition(
        cartStateEmpty => cartStateEmpty,
        cartStateActive => cartStateActive.Pay(paidAmount),
        cartStatePaid => cartStatePaid
        );
}

As you can see, this approach solves all the problems discussed earlier:

  • It is completely idiot proof. A client of the interface always gets a valid value back. The client cannot mess things up by forgetting to cast or forgetting to check for null.
  • Illegal states are not even representable. A client cannot call the “wrong” method, such as calling “Pay” on an empty cart. The compiler will not allow it. Because of this, there is no need for runtime errors or “not implemented” exceptions.
  • If the number of states ever changes to four say, just add a new parameter to the Transition method. The Transition method will now take four lambdas, so all your existing code will fail to compile. This a good thing! You cannot accidentally forget to handle a state.
  • The code is simple and low complexity. Just as in the second approach above, each state only implements the methods it needs to, and there are no if statements or special case handlers anywhere.

In a way, this is what the visitor pattern was trying to get at in its complicated way, but the use of inline lambdas rather than whole visitor classes reduces the complexity immensely.

Introducing the T4 template

Now that we understand how the states work in conjunction with the top level interface, we can create them in C#.

But there is a lot of boilerplate – so I have created a T4 template that generate the states for you automatically.

Creating new types using the T4 template

To setup, first download the FoldStates.ttinclude and add it to your project somewhere.

Then to create a new type:

  1. Create a template file and give the filename the name of the interface type, such as “ICartState.tt”
  2. Include the code below, and define each state that you want in the array, as shown.
1
2
3
4
<#
var states = new [] {"CartStateEmpty", "CartStateActive","CartStatePaid"};
#>
<#@ include file="..\FoldStates.ttinclude" #>

Visual Studio will then generate you a file with code like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
partial interface ICartState
{
    ICartState Transition(Func<CartStateEmpty, ICartState> cartStateEmpty, Func<CartStateActive, ICartState> cartStateActive, Func<CartStatePaid, ICartState> cartStatePaid);
    void Action(Action<CartStateEmpty> cartStateEmpty, Action<CartStateActive> cartStateActive, Action<CartStatePaid> cartStatePaid);
    TObject Func<TObject>(Func<CartStateEmpty, TObject> cartStateEmpty, Func<CartStateActive, TObject> cartStateActive, Func<CartStatePaid, TObject> cartStatePaid);
}

partial class CartStateEmpty : ICartState
{
    ICartState ICartState.Transition(Func<CartStateEmpty, ICartState> cartStateEmpty, Func<CartStateActive, ICartState> cartStateActive, Func<CartStatePaid, ICartState> cartStatePaid)
    {
        return cartStateEmpty(this);
    }

    void ICartState.Action(Action<CartStateEmpty> cartStateEmpty, Action<CartStateActive> cartStateActive, Action<CartStatePaid> cartStatePaid)
    {
        cartStateEmpty(this);
    }

    TObject ICartState.Func<TObject>(Func<CartStateEmpty, TObject> cartStateEmpty, Func<CartStateActive, TObject> cartStateActive, Func<CartStatePaid, TObject> cartStatePaid)
    {
        return cartStateEmpty(this);
    }
}

partial class CartStateActive : ICartState
{
    ICartState ICartState.Transition(Func<CartStateEmpty, ICartState> cartStateEmpty, Func<CartStateActive, ICartState> cartStateActive, Func<CartStatePaid, ICartState> cartStatePaid)
    {
        return cartStateActive(this);
    }

    void ICartState.Action(Action<CartStateEmpty> cartStateEmpty, Action<CartStateActive> cartStateActive, Action<CartStatePaid> cartStatePaid)
    {
        cartStateActive(this);
    }

    TObject ICartState.Func<TObject>(Func<CartStateEmpty, TObject> cartStateEmpty, Func<CartStateActive, TObject> cartStateActive, Func<CartStatePaid, TObject> cartStatePaid)
    {
        return cartStateActive(this);
    }
}

partial class CartStatePaid : ICartState
{
    ICartState ICartState.Transition(Func<CartStateEmpty, ICartState> cartStateEmpty, Func<CartStateActive, ICartState> cartStateActive, Func<CartStatePaid, ICartState> cartStatePaid)
    {
        return cartStatePaid(this);
    }

    void ICartState.Action(Action<CartStateEmpty> cartStateEmpty, Action<CartStateActive> cartStateActive, Action<CartStatePaid> cartStatePaid)
    {
        cartStatePaid(this);
    }

    TObject ICartState.Func<TObject>(Func<CartStateEmpty, TObject> cartStateEmpty, Func<CartStateActive, TObject> cartStateActive, Func<CartStatePaid, TObject> cartStatePaid)
    {
        return cartStatePaid(this);
    }
}

Using the new type

A few things to note about the generated code:

  • There are three generated methods: Transition, Action and Func. I’ll explain the purpose of each one shortly.
  • All the classes are partial. After creating the states, you should then create separate files for the partial classes so you can write some real code!

For the shopping cart example, let’s look at each state in turn.

The CartStateEmpty state

As described above, you should create a separate file CartStateEmpty.cs and create code like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
public partial class CartStateEmpty
{
    internal CartStateEmpty()
    {
    }

    public ICartState Add(Product item)
    {
        var newItems = new[] { item };
        var newState = new CartStateActive(newItems);
        return newState;
    }
}

This state has no items. The only thing you can do with this state is add a product.

To add a product, create a new list of products and pass it into the constructor of the CartStateActive class.

Note that the Add method returns an ICartState not a CartStateActive. This is critical! The caller should not know the exact state of the cart, just that it is some kind of cart. You’ll see why this is so important shortly.

The CartStateActive state

Again, you should create a separate file CartStateActive.cs and create code like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public partial class CartStateActive
{
    public CartStateActive(IEnumerable<Product> items)
    {
        Items = items;
    }

    public IEnumerable<Product> Items { get; private set; }

    public ICartState Add(Product item)
    {
        var newItems = new List<Product>(Items) { item };
        return new CartStateActive(newItems);
    }

    public ICartState Remove(Product item)
    {
        var newItems = new List<Product>(Items);
        newItems.Remove(item);
        return newItems.Count > 0
            ? (ICartState)new CartStateActive(newItems)
            : new CartStateEmpty();
    }

    public ICartState Pay(decimal amount)
    {
        return new CartStatePaid(Items, amount);
    }
}

This state does have a list of items, and supports three methods: Add, Remove, and Pay.

To add a product, create a new list of products and pass it into the constructor of the CartStateActive class. Again note that the Add method returns an ICartState not a CartStateActive.

To remove a product, remove it from the existing list of products and pass either into the constructor of the CartStateActive class, or if the list is empty, return a new CartStateEmpty.
Again note that this method returns an ICartState. The (ICartState) cast is needed to make the ternary expression happy, unfortunately.

Finally, the Pay method creates a CartStatePaid and returns it. Once again, the return type is ICartState.

The CartStatePaid state

Finally, implement CartStatePaid.cs with code like this:

1
2
3
4
5
6
7
8
9
10
11
public partial class CartStatePaid
{
    public CartStatePaid(IEnumerable<Product> items, decimal amount)
    {
        Items = items.ToList();
        Amount = amount;
    }

    public IEnumerable<Product> Items { get; private set; }
    public decimal Amount { get; private set; }
}

CartStatePaid is immutable – you can get the list of items and payment amount, but you can’t change them.

Now that you have seen all the implementations, it is worth pointing out another great benefit of this approach. In the implementation for each state, there are hardly any conditionals or branches! You’ll find that this state-based approach really reduces code complexity because each method only has to deal with one state.

Putting it together with some tests.

Lets see how this code can be used in practice.

First, lets start with a simple test case: “If you add a item to an empty cart you get an active cart with one item”.

Here’s the code for this test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Test]
public void WhenEmptyCartAndAddItemExpectActiveCartWithOneItem()
{
    var emptyCart = new CartStateEmpty();

    var newState = emptyCart.Add(Product.ProductX);

    var isActiveState = newState.Func(
        cartStateEmpty => false,
        cartStateActive => true,
        cartStatePaid => false
        );
    Assert.IsTrue(isActiveState);

    var itemCount = newState.Func(
        cartStateEmpty => -1,
        cartStateActive => cartStateActive.Items.Count(),
        cartStatePaid => -1
        );
    Assert.That(itemCount, Is.EqualTo(1));
}

You can see here that we are using the Func method and passing in three lambdas. To test what state the cart is in, we return true or false for each lambda. And to count the items, we return a int.

Note that if we had the following code, we would get an compiler error:

1
2
3
4
5
var itemCount = newState.Func(
   cartStateEmpty => cartStateEmpty.Items.Count(),
   cartStateActive => cartStateActive.Items.Count(),
   cartStatePaid => cartStatePaid.Items.Count()
   );

The reason is that cartStateEmpty does not have an Items property. This is a key concept – you cannot call a method accidentally using this approach. The caller cannot forget to cast, or forget to handle a state. The interface enforces this.

Let’s look at another test: “If you pay for an active cart you get an paid cart with the same number of items and the same amount paid”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[Test]
public void WhenActiveCartWithTwoItemsAndPayExpectPaidCart()
{
    var activeCart = new CartStateActive(new[] { Product.ProductX, Product.ProductY });

    const decimal paidAmount = 12.34m;
    var newState = activeCart.Pay(paidAmount);

    var isPaidState = newState.Func(
        cartStateEmpty => false,
        cartStateActive => false,
        cartStatePaid => true
        );
    Assert.IsTrue(isPaidState);

    var itemCount = newState.Func(
        cartStateEmpty => -1,
        cartStateActive => -1,
        cartStatePaid => cartStatePaid.Items.Count()
        );
    Assert.That(itemCount, Is.EqualTo(2));

    var actualPaidAmount = newState.Func(
        cartStateEmpty => -1,
        cartStateActive => -1,
        cartStatePaid => cartStatePaid.Amount
        );
    Assert.That(actualPaidAmount, Is.EqualTo(paidAmount));
}

Note that in the last assertion, we are getting the cartStatePaid.Amount. This property does not exist for the other two states, and as above, if we accidentally try to call it, we will get a compiler error.

So you have seen how the Func method works. The Action method is exactly the same, but returns void instead.

The Transition method

Now let’s look at the Transition method and see how it is used.

Here is the code that adds a product to a cart.

1
2
3
4
5
6
7
8
public ICartState AddProduct(ICartState currentState, Product product)
{
    return currentState.Transition(
        cartStateEmpty => cartStateEmpty.Add(product),
        cartStateActive => cartStateActive.Add(product),
        cartStatePaid => cartStatePaid
        );
}

Some things to note:

  • The input (currentState) is a ICartState because we don’t know what state it will be.
  • The return type is also a ICartState because we don’t know what the state will be after adding the item. Well, actually we do in this particular case, but generally we won’t. So for example RemoveItem could return either an empty state or an active state.
  • The input value is not ‘edited in place’ but transformed into a new value. In the state implementations above, you’ll have seen that all the states are immutable. It’s not just a ‘nice-to-have’, but is critical for these kinds of state based transitions.

A particular important point is that all three lambdas return something. In this case, we can call Add for the empty and active states. But what about the paid state? It doesn’t have an Add method!

This is where we are forced to make a choice: we could throw an exception or log an error. But in this case, we will ignore it. We have to return a valid cart, so we can’t return null. Instead, we’ll just return the cart that was passed in.

Now earlier I talked about a similar decision for the empty state – should it throw an exception or silently ignore the problem. There is a big difference however between that case and this one. however. In that case, the decision was inside the empty state’s implementation. The decision would be applicable to all callers.

But in this approach, the decision is up to the caller, not the implementor. It is up to the caller to decide what to do, and it can provide different behavior in different circumstances.

Finally, I hope you can now see why the Add method returned an ICartState rather than a CartStateActive . It is because all the the lambdas must return the same type, and in the case of transition from one state to another, that type will always be ICartState.

Ok, let’s see how Remove and Pay would be implemented:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public ICartState RemoveProduct(ICartState currentState, Product product)
{
    return currentState.Transition(
        cartStateEmpty => cartStateEmpty,
        cartStateActive => cartStateActive.Remove(product),
        cartStatePaid => cartStatePaid
        );
}

public ICartState Pay(ICartState currentState, decimal amount)
{
    return currentState.Transition(
        cartStateEmpty => cartStateEmpty,
        cartStateActive => cartStateActive.Pay(paidAmount),
        cartStatePaid => cartStatePaid
        );
}

Very similar to the AddProduct method, except that in these cases, we can only call methods on the active state.

To see how these methods might be combined together, we might have a set of transitions that looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Test]
public void TransitionExample()
{
    var currentState = new CartStateEmpty() as ICartState;

    //add some products from the UI, say
    currentState = AddProduct(currentState, Product.ProductX);
    currentState = AddProduct(currentState, Product.ProductY);
    currentState = RemoveProduct(currentState, Product.ProductY);

    //pay from the UI, say
    const decimal paidAmount = 12.34m;
    currentState = Pay(currentState, Product.ProductY);
}

Another example: Email Addresses

We’ll quickly look at another way these states can be used, namely for handling a domain object that might have distinct ‘valid’ and ‘invalid’ states that mustn’t be mixed up.

Consider an email address. It can valid (conforming to the RFC spec) or invalid. We might want to allow both states in our domain, but on the other hand, only allow valid emails to be sent to.

The IEmailAddress template

We’ll call the common state IEmailAddress. We will use the EmailAddress state to represent any email address, including invalid ones, and the ValidEmailAddress state to mean only valid emails.

So here is the template:

1
2
3
4
<#
var states = new [] {"EmailAddress", "ValidEmailAddress"};
#>
<#@ include file="..\FoldStates.ttinclude" #>

This generates the same kind of code as we saw for the shopping cart states:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
partial interface IEmailAddress
{
    IEmailAddress Transition(Func<EmailAddress, IEmailAddress> emailAddress, Func<ValidEmailAddress, IEmailAddress> validEmailAddress);
    void Action(Action<EmailAddress> emailAddress, Action<ValidEmailAddress> validEmailAddress);
    TObject Func<TObject>(Func<EmailAddress, TObject> emailAddress, Func<ValidEmailAddress, TObject> validEmailAddress);
}

partial class EmailAddress : IEmailAddress
{
    IEmailAddress IEmailAddress.Transition(Func<EmailAddress, IEmailAddress> emailAddress, Func<ValidEmailAddress, IEmailAddress> validEmailAddress)
    {
        return emailAddress(this);
    }

    void IEmailAddress.Action(Action<EmailAddress> emailAddress, Action<ValidEmailAddress> validEmailAddress)
    {
        emailAddress(this);
    }

    TObject IEmailAddress.Func<TObject>(Func<EmailAddress, TObject> emailAddress, Func<ValidEmailAddress, TObject> validEmailAddress)
    {
        return emailAddress(this);
    }
}

partial class ValidEmailAddress : IEmailAddress
{
    IEmailAddress IEmailAddress.Transition(Func<EmailAddress, IEmailAddress> emailAddress, Func<ValidEmailAddress, IEmailAddress> validEmailAddress)
    {
        return validEmailAddress(this);
    }

    void IEmailAddress.Action(Action<EmailAddress> emailAddress, Action<ValidEmailAddress> validEmailAddress)
    {
        validEmailAddress(this);
    }

    TObject IEmailAddress.Func<TObject>(Func<EmailAddress, TObject> emailAddress, Func<ValidEmailAddress, TObject> validEmailAddress)
    {
        return validEmailAddress(this);
    }
}

Implementing the states

We’ll start by adding a property to the interface that both states will have to implement:

1
2
3
4
public partial interface IEmailAddress
{
    string Address { get; }
}

Next, we’ll implement the general email address.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public partial class EmailAddress
{
    public EmailAddress(string emailAddress)
    {
        Address = emailAddress;
    }

    public string Address { get; protected set; }

    /// <summary>
    /// Create a new email address
    /// </summary>
    static public IEmailAddress New(string emailAddress)
    {
        try
        {
            // do the validation in the ValidEmailAddress itself so that it is impossible to create an invalid one
            return new ValidEmailAddress(emailAddress);
        }
        catch (ArgumentException)
        {
            return new EmailAddress(emailAddress);
        }
    }
}

Note that it implements a static New method that returns either a ValidEmailAddress or a invalid EmailAddress.

Finally, the ValidEmailAddress implementation is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public partial class ValidEmailAddress
{
    public ValidEmailAddress(string emailAddress)
    {
        // do validation here
        if (string.IsNullOrEmpty(emailAddress) || !emailAddress.Contains("@"))
        {
            throw new ArgumentException("emailAddress");
        }

        Address = emailAddress;
    }

    public string Address { get; protected set; }

    /// <summary>
    /// This method is only available for the ValidEmailAddress
    /// </summary>
    public void SendMessage(string subject)
    {
        var from = "me@example.com";
        Console.WriteLine("To: {0} From: {1} Re: {2}", Address, from, subject);
    }
}

As you can see, only the ValidEmailAddress implements the SendMessage method.

Using the email address

Let’s look at two test cases to see how this works in practice.

Test: If the address does not contain “@”, the address is not valid

1
2
3
4
5
6
7
8
[Test]
public void OnCreationWithNoAtSignExpectInvalidAddress()
{
    var email = EmailAddress.New("no at sign");

    var isValid = email.Func(invalid => false, valid => true);
    Assert.IsFalse(isValid);
}

Test: If the address does contain “@”, the address is valid

Yes, the validation is a bit crude!

1
2
3
4
5
6
7
8
[Test]
public void OnCreationWithAtSignExpectValidAddress()
{
    var email = EmailAddress.New("x@example.com");

    var isValid = email.Func(invalid => false, valid => true);
    Assert.IsTrue(isValid);
}

Test: You cannot send email to an invalid address

1
2
3
4
5
6
7
8
9
10
[Test]
public void CannotSendEmailToInvalidAddress()
{
    var email = EmailAddress.New("x@example.com");

    email.Action(
        invalid => { },
        valid => valid.SendMessage("hello")
        );
}

Again, note that you cannot bypass the type safety. What would happen if you attempted to send an email to an invalid address like this:

1
2
3
4
5
6
7
8
9
10
[Test]
public void CannotSendEmailToInvalidAddress()
{
    var email = EmailAddress.New("x@example.com");

    email.Action(
        invalid => invalid.SendMessage("hello"),
        valid => valid.SendMessage("hello")
        );
}

Try it and see!

Summary

Well, I hope you found this interesting and useful. It’s definitely a different way of thinking if you are not familiar with functional programming.

BTW, you might be wondering why the template is called “FoldStates”. The answer is that this technique of having a different function for each case is more generally called a ”fold” (or catamorphism if you want to get fancy).

In this example, we have only just touched on the power of folds. In a language like F# that supports recursive data structures, they are a key part of a functional programmers toolbelt.

Getting the code

You can get the code two ways:

  • Download the template only: FoldStates.ttinclude.
  • or, from the git repository for this blog (see the about page). This has a full set of examples and unit tests.

Comments