# Algebraic Data Types in C#

## Introduction

An algebraic type system is a type system that uses algebraic data types (ADTs) to represent relations between data, specifically the notions of "and" and "or". ADTs are composite types formed by combining other types, and there are two main kinds of ADTs: sum types and product types. Sum types are also known as tagged or disjoint unions, coproduct types, or variant types, and they are used to represent a value that is exactly one of many possible types. Product types are tuples and records, and they are used to represent multiple types combined together. Together, sum types and product types are like a dynamic duo for encoding business logic, helping to make good things possible and bad things impossible.

### Audience

This blog post is intended to be read by C# developers of any level; beginner, intermediate, and advanced. All readers will have something to learn from this blog post.

### Prerequisites

To fully benefit from this blog post, I assume the reader has a basic working knowledge of classes, records, and generics. I introduce the basic concepts needed to understand algebraic types when we need them.

### Objectives

By the end of this blog post, the reader should be able to answer the following questions:

* What is an algebraic-type system?
    
* What are algebraic types?
    
* What are sum types?
    
* What are product types?
    

### Setup

In this blog post, I will use the [LanguageExt library](https://github.com/louthy/language-ext) since it provides many features that turn the C# language into a fully functional language. C#'s support for sum types is still lacking, and this library will make it easier to introduce these concepts.

## Sum Types

Sum types are a type of algebraic data type (ADT) that represent a value that is exactly one of many possible types. They are also known as tagged or disjoint unions, coproduct types, or variant types. The values of a sum type are typically grouped into several classes, called variants. Each variant has its own constructor, which takes a specified number of arguments with specified types. The set of all possible values of a sum type is the set-theoretic sum, i.e., the disjoint union, of the sets of all possible values of its variants.

The name "sum type" comes from the fact that the number of values of A + B is the sum of the number of values of A and B. Sum types are helpful for modeling situations where a value can be one of several possible types, such as the result of a function that can return different types of values depending on its input.

In programming languages that support sum types, each variant of a sum type is usually associated with a tag or label that identifies it. This tag determines which variant a value belongs to and can be used in pattern matching to extract the value. Sum types are often used in functional programming languages like Haskell, OCaml, and F#.

### Defining Sum types

To understand Sum types, we are going to follow a series of examples. Let's start simple. Imagine that you are working on a project that needs to model people's genders. Let us say we want to write a function that takes in the gender and generates a title for a given person. Assuming that we represent gender as a string, here is our first implementation.

```csharp
public string GenerateTitle(string gender)
{
    if(gender == "Male")
        return "Mr";
    else if(gender == "Female")
        return "Mrs";
    else
        return "Mr/Mrs"
}
```

There are lots of problems that this function has, let us try to go through them.

* **Invalid arguments**. Note that the gender is modelled as a string. This means we can pass anything to this function that is a string. This contains garbage inputs, like `"kl98"` .
    
* **Missing abstraction**. Another problem with this function is that it fails to capture a domain concept, which is a sign of poor domain modelling. Gender is a concept in the domain model, but there is explicit mention of it here. This means the type system won't help us eliminate some errors.
    

**Enumerations**

Seasoned C# programmers will notice that this is a good use case for enumerations. Since the `Gender` type is finite, and contains only three values, we can represent it using an enumeration. We can then pattern match on the enumeration value.

```csharp
public enum Gender { Male, Female, Other}

public string GenerateTitle(Gender gender)
{
    if(gender == Gender.Male)
        return "Mr";
    else if(gender == Gender.Female)
        return "Mrs";
    else
        return "Mr/Mrs"
}
```

This implementation has advantages over the previous one, as explained below.

* We now know that our function takes in only valid values since the `Gender` enumeration limits the kind of values passed in, not just any string as before.
    
* Our code now speaks in the language of the domain, also known as the `ubiquotous language` .
    

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Programming is the process of building abstractions. These abstractions are models of the entities in the real world. It is always better to explicitly mention these abstractions in your domain model. Think of your program as a DSL for a given domain.</div>
</div>

Note that this function can also be coded as a dictionary since we can explicitly enumerate the input arguments. Dictionaries are a good representation of arbitrary functions.

```csharp
var title = new Dictionary<Gender, string> { 
    [Gender.Male] = "Mr",
    [Gender.Female] = "Mrs",
    [Gender.Other] = "Mr/Mrs"
}
```

You may be forced to mistake enumerations for Sum types but that is not true. To understand where enumerations fall short, let us assume that we want to model a system that supports 3 payment systems.

* Cash payment
    
* Cheque payment
    
* Card payment
    

We can't use an enumeration here because each payment type needs different data to be provided. Enumeration values are just spiced up `ints` that don't carry more information with them.

Let us assume that each payment type needs the following information:

| Payment type | Data needed |
| --- | --- |
| Cash | Amount |
| Card | Card number, CardType, and Amount |
| Cheque | Cheque number, Owner name, and Amount |

It is in situations like these that Sum types really shine. We can see that the payment type can be one of the above at a time.

### Payment type definition

```haskell
data CardType = ATM | Debit | Credit
data Payment = Cash decimal
               | Card string CardType decimal
               | Cheque string string decimal
```

In the above snippet, we used the Haskell syntax to define the Sum type. Let us go through each concept in the above definition to get an understanding of what is happening.

* **Type constructor**. In the above code snippet, `CardType` and `Payment` are known as type constructors. This is because they construct new types. They extend our type system with new types. To be able to encode the domain language into your code, you will need to create new types no matter how small they will be. You can view a type as a set.
    
* **Data constructor.** `Cash` , `Card` and `Cheque` are known as data constructors. This is because it is these constructs that construct the concrete values of the type. You can think of them as set elements. From the above definition, there are 3 possible ways of constructing elements of the set `Payment` and these are the 3 provided data constructors.
    
* **pipe.** The pipe operator is used to separate the different available alternatives for creating values.
    

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">You can think of the data constructors as functions that take in given arguments and return an object of type <code>Payment</code>. Check the table below for their signatures</div>
</div>

| Data constructor | Signature |
| --- | --- |
| Cash | (decimal) --&gt; Payment |
| Card | (string, CardType, decimal) --&gt; Payment |
| Cheque | (string, string, decimal) --&gt; Payment |

```csharp
 public enum CardType { ATM, Debit, Credit }

 [Union]
 public interface Payment
 {
     Payment Cash(decimal amount);
     Payment Card(CardNumber number,  CardType cardType, Amount amount);
     Payment Cheque(ChequeNumber number,  OwnerName ownerName, Amount amount);
 }
```

Now that I have defined the different variants of our `Payment` type, I now define the `Payment` type using the `OneOf` library as shown below.

### Pattern matching

Now that we have defined our `Payment` type. How can we use it in our domain to settle a payment? To do this, I define a function that takes in the payment strategy and settles the payment.

```csharp
public static Amount GetAmount(Payment payment)
      => payment switch {
            Cash cash => cash.Amount,
            Card card => card.Amount,
            Cheque cheque => cheque.Amount,
             _ => 0m
       };
```

Here are some points to note about this function.

* Since the input argument can take on three different shapes, i.e. (Cash, Card and Cheque) we need to handle each of them. I do this by using the `Match` method defined in the `OneOf` . This method takes `func` objects that correspond to the different methods that handle each of the variants. **This is called Pattern matching.**
    
* I define a type alias for the `ValueTuple` . This is because we can not use void as a return type in functional programming since the void type is empty and hence can't have any values.
    

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">In case you fail to handle any of the variants, your code will not compile. This is one advantage of sum types over inheritance. All cases must be handled for your code to compile.</div>
</div>

### Use cases of Sum types in DDD

Sum types are a useful tool in DDD for modeling complex business logic in a type-safe way. Here are some scenarios where sum types can be particularly useful in DDD.

**Status codes**  
In many domains, there are a limited number of possible statuses that an entity can have. For example, an order might be "pending", "shipped", or "delivered". By defining a sum type for these statuses, you can ensure that all code that deals with orders handles all possible statuses correctly. This can help prevent bugs and make the code easier to reason about.

```csharp
enum OrderStatus = { Pending, Shipped, Delivered }
```

**Error handling**  
In DDD, it's common to define custom exceptions for specific error conditions. By using a sum type to represent the possible error conditions, you can ensure that all code that handles exceptions handles all possible error conditions correctly. This can help prevent bugs and make the code easier to reason about.

Let's assume that we are working in the banking domain. One of the most common operations in this domain is withdrawing money. We all know that a user can only withdraw money if they have it in their account. We also agree that there are lots of errors that can occur in the banking domain; in this example, we shall only code two of them.

```csharp
public interface BankError
{
     BankError InvalidAccount(AccountNumber number);
     BankError InsufficientFunds(Amount currentBalance, Amount requestedAmount);
}
```

Now let's define the domain service that withdraws money from the account. This is modeled in the following snippet:

```csharp
E
```

**State machines**  
In some domains, entities can have a complex state machine that determines what actions are allowed in each state. By defining a sum type for the possible states and the actions that are allowed in each state, you can ensure that all code that deals with the entity handles the state machine correctly. This can help prevent bugs and make the code easier to reason about.

**Workflow steps**  
In some domains, entities can go through a series of steps to complete a process. By defining a sum type for the possible steps and the data that is required for each step, you can ensure that all code that deals with the workflow handles all possible steps correctly. This can help prevent bugs and make the code easier to reason about.

## Conclusion
