How to Write Functional Code in C#: An Example

by Zoran Horvat

C# is a multi-paradigm language. Among others, it allows us to write procedural or object-oriented code, but it also supports full-blown functional coding. In this article, we will see what benefits we can get from writing functional code in C#. We will see what it means to write functional code and how we can accomplish that using standard C# syntax.

In this article we will demonstrate functional approach to one simple example, which comes from practice. The first part of the article will deal with actual implementation, which is running in production right now, as a matter of fact. After that, we will start refactoring the solution to a much more flexible one. The final product will be a class that is completely designed with respect to functional programming paradigm.

Problem Statement

Consider a problem of calculating a control digit in some document number. Control digits are used to minimize impact of human errors. Suppose that documents are accepted by clerks and that these numbers sometimes experience changes caused by human error. By inadvertently changing just one digit in the document number, we can cause costly consequences. The situation would be significantly worse if that particular number has already been assigned to a different document.

Correcting the error would first require to understand which document has been entered with the wrong number. This may involve both document holders to appear with their papers in order to figure which one got the incorrect printout. Then additional evidence might be required to decide which of the two apparently valid documents is the faulty one and needs to be invalidated. Now the invalidation comes. It doesn't mean just to tear the document in pieces, but also to annul its consequences. Other documents may have been produced in the meanwhile, referring to this one as their source. The situation becomes increasingly bad if other documents produced legal or financial effects based on a document that is about to be declared void.

To minimize all that mess, document numbers often contain a control digit. This digit typically appears at the end of the number and it is calculated from digits that precede it. Normally, number consists of decimal digits 0 thru 9 only, and so is another decimal digit used as control digit. Result is that if one of the preceding digits has been changed, the control digit would immediately indicate that there is the problem, because control digit would be different. If only one digit has been modified, control digit would definitely indicate the problem. But if two or more digits have been changed, then their combined effect could sometimes be that the same control digit is produced by pure coincidence. Having ten possible values for the control digit at hand, it turns out that single control digit will detect 100% of mistakes made on a single digit, as well as 90% of mistakes made on more than one digit simultaneously. Also note that mistakes made on control digit count as well.

Here is one algorithm applied to calculate control digit. This algorithm is actually in use in real world. It is used to calculate control digit for an insurance policy numbers issued by one particular organization that joins several insurance companies. The document number is typed manually so many times, and the idea to put in a control digit was probably the right decision.

Policy number, excluding the control digit, is nine digits long. It only consists of decimal digits 0 thru 9. The tenth digit would be the control digit and it would be appended at the end to form a complete policy number. Control digit is calculated in three steps. The first step is to sum up all the preceding digits. But, there is one additional requirement: every other digit should be multiplied by three before being added to the sum.

Take policy number 123456789 as an example. The first step in calculating the control digit would then be to sum up the digits, while at the same time honoring the request to triple every other digit. So the sum is 1 + 3*2 + 3 + 3*4 + 5 + 3*6 + 7 + 3*8 + 9 = 85.

The next step is to take modulo 11 of this number: 85 mod 11 = 8. The third step is applied only when the result of previous step is 10. In that case control digit value should be 1. Otherwise, take the result obtained as the control digit and append it to the insurance policy number.

Before producing the code, there was one more nuisance about this whole task. The function had to be implemented in T-SQL. Here we are still talking about a real project for the real customer. Actual client of the stored procedure that would calculate the control digit would be another SQL Server stored procedure. Any idea to write a good old C# function was out before it came to the picture.

Anyway, as you will see, stored procedure implementation will serve as a fabulous prelude to solving the same problem functionally.

Implementation as Stored Procedure

Below is the stored procedure which implements the requirements literally. The stored procedure expects nine characters on input and produces an integer which is the control digit for the given trimmed insurance policy number.

CREATE DATABASE Insurance
GO

USE Insurance

CREATE PROCEDURE CalculateControlDigit @policyNumber CHAR(9)
AS
BEGIN

    DECLARE @controlDigit INT = 0
    DECLARE @currentDigit INT
    DECLARE @i INT = 1
    DECLARE @sum INT = 0

    WHILE @i <= 9
        BEGIN
            SET @currentDigit = CAST(SUBSTRING(@policyNumber, @i, 1) AS INT)
            IF @i % 2 = 0
                SET @sum = @sum + 3 * @currentDigit
            ELSE
                SET @sum = @sum + @currentDigit
            SET @i = @i + 1
        END

    SET @controlDigit = @sum % 11

    IF @controlDigit > 9
        SET @controlDigit = 1

    SELECT @controlDigit

END
GO

Let's try this stored procedure:

EXEC CalculateControlDigit '123456789'

When called with this argument, the procedure returns value 8, as expected.

The whole idea about calculating the control digit is to avoid common mistakes. There are some typical mistakes that people make when typing numbers. Most frequent mistake is to move a finger to the left or to the right of the target key, producing a digit which is by one less or by one greater than desired. Another typical mistake occurs on numeric keypad and that is to miss a row. This causes a digit which is by 3 greater or less than desired. Finally, one more mistake that commonly occurs in practice is to swap places of two digits in the number. Let’s see how this algorithm copes with these two mistakes.

EXEC CalculateControlDigit '123556789'

This time, the stored procedure returns value 0. By comparing this control digit with actual control digit from the whole insurance policy number, we can figure that the number was mistyped.

Another mistake would be to increase or decrease one digit by 3:

EXEC CalculateControlDigit '123453789'

Once again, this has caused the control digit to change, this time producing value 1 instead of 8.

And, one more example for the end – swapping two digits:

EXEC CalculateControlDigit '124356789'

Thanks to having to multiply one of the two digits by factor of three, new control digit value has changed. It is 6, instead of 8.

We conclude that stored procedure works fine and that the algorithm really protects us from the three most common mistakes. After this step, we will start improving the solution. At first we will keep the stored procedure in use, but try to write it in a little bit better way. After that, we will pretend that there is no hard requirement to implement the solution as stored procedure and we will move the code to C#. This will let us apply functional concepts to the same problem.

Improving the Procedure

The first step in improving existing stored procedure code is to ask the proper question – what is wrong with the code? One thing that annoys me is branching. If statements complicate control flow and make it hard to read and to understand.

In the solution above there are two if statements. One is there to control multiplication factor for every digit separately. Another is near the end of the stored procedure and it deals with result 10 by converting it to actual result 1.

The first if statement can easily be eliminated by introducing another variable – multiplicative factor:

CREATE PROCEDURE CalculateControlDigit @policyNumber CHAR(9)
AS
BEGIN

    DECLARE @controlDigit INT = 0
    DECLARE @currentDigit INT
    DECLARE @i INT = 1
    DECLARE @sum INT = 0
    DECLARE @factor INT = 1

    WHILE @i <= 9
        BEGIN
            SET @currentDigit = CAST(SUBSTRING(@policyNumber, @i, 1) AS INT)
            SET @sum = @sum + @factor * @currentDigit
            SET @factor = 4 - @factor
            SET @i = @i + 1
        END

    SET @controlDigit = @sum % 11

    IF @controlDigit > 9
        SET @controlDigit = 1

    SELECT @controlDigit

END
GO

Look at the loop now. It has obviously become much simpler than before. It is much easier to understand what it does when there is no if-then-else statement to obscure view. This time it is obvious that every digit is multiplied by a factor and added to the sum. Multiplicative factors applied to digits obviously form a sequence looking like 1, 3, 1, 3, 1, 3, and so on.

Let’s see the second if statement now. This statement jumps in when modulo operation returns value 10. That value is translated to value 1, while all other values are left intact. Can we figure any better method of doing the same thing? Actually, yes, we can. If we consider all possible results of the modulo operation to be two-digit numbers, all of them having a leading zero except number 10, then we can transform the if statement into operation of summing up the two digits in the resulting number. Here is the modified code:

CREATE PROCEDURE CalculateControlDigit @policyNumber CHAR(9)
AS
BEGIN

    DECLARE @controlDigit INT = 0
    DECLARE @currentDigit INT
    DECLARE @i INT = 1
    DECLARE @sum INT = 0
    DECLARE @factor INT = 1

    WHILE @i <= 9
        BEGIN
            SET @currentDigit = CAST(SUBSTRING(@policyNumber, @i, 1) AS INT)
            SET @sum = @sum + @factor * @currentDigit
            SET @factor = 4 - @factor
            SET @i = @i + 1
        END

    SET @controlDigit = @sum % 11
    SET @controlDigit = (@controlDigit / 10) + (@controlDigit % 10)

    SELECT @sum, @controlDigit

END
GO

Stored procedure is now relieved from branching code. The only remaining part that is actually branching is the while loop. Obviously Transact-SQL is not the best place to try removing the while loop, so let’s step into C# right now and continue coding there.

Implementing Procedure in C#

In this section we will produce the first C# implementation of the function that calculates the control digit from a given insurance policy number. You might be annoyed by the section title – implementing procedure in C#. Having stored procedure left behind, we should now be dealing with “real code”, I mean object-oriented code, right? Well, not so fast. As it will soon become apparent, the first implementation of this function in C# will be no different from SQL stored procedure, which really is a pity.

Let’s see what this function looks like when turned into C#:

static int CalculateControlDigit(string number)
{

    int sum = 0;
    int factor = 1;

    for (int i = 0; i < number.Length; i++)
    {
        int currentDigit = number[i] - '0';
        sum += factor * currentDigit;
        factor = 4 - factor;
    }

    int controlDigit = sum % 11;
    controlDigit = (controlDigit  10) + (controlDigit % 10);

    return controlDigit;

}

This function is literally copied from Transact-SQL. And it looks like that, to be honest. This kind of code has nothing to do with higher-level object-oriented programming language like C#. So let’s start changing this function in order to make something better.

First change that we will make is to remove the string argument. It may have been a valid requirement in the database implementation, but now that we are dealing with the strongly typed language we can easily constrain input to integer numbers. Due to length of the insurance policy number (ten digits, including the control digit), we decide to use 64-bit unsigned integers. But this change requires us to implement splitting the number into its digits:

static int CalculateControlDigit(UInt64 number)
{

    int sum = 0;
    int factor = 1;

    int[] digits = new int[9];
    for (int i = 0; i < 9; i++)
    {
        digits[8 - i] = (int)(number % 10);
        number = 10;
    }

    for (int i = 0; i < digits.Length; i++)
    {
        sum += factor * digits[i];
        factor = 4 - factor;
    }

    int controlDigit = sum % 11;
    controlDigit = (controlDigit  10) + (controlDigit % 10);

    return controlDigit;

}

The function has actually become longer, but that is due to inability to split the number into digits using built-in methods. Nevertheless, we can easily move that ugly piece of code into a separate method and be done with it. What attracts attention right now is that the factor can easily be calculated in every iteration of the loop. There is no need to keep it memorized:

static int[] ToDigits(UInt64 number)
{

    int[] digits = new int[9];

    for (int i = 0; i < 9; i++)
    {
        digits[8 - i] = (int)(number % 10);
        number = 10;
    }

    return digits;

}

static int CalculateControlDigit(UInt64 number)
{

    int sum = 0;
    int[] digits = ToDigits(number);

    for (int i = 0; i < digits.Length; i++)
        sum += (1 + 2 * (i % 2)) * digits[i];

    int controlDigit = sum % 11;
    controlDigit = (controlDigit  10) + (controlDigit % 10);

    return controlDigit;

}

The function is shrinking again. That is good. Multiplicative factor has been replaced by a fairly cryptic piece of arithmetic. But that was done with a reason, and the reason is to remove the last piece of non-linear control flow from this function. The for loop is the last part of the function which doesn’t just execute and move on. Instead, this loop keeps running in circles until all digits have been exhausted.

This is the point where functional programming begins. In the next section we will transform this procedural solution into functional solution.

Functional Approach

In order to remove the for loop, we decide to use LINQ to Objects. Extension methods provided by this component of the .NET Framework implement the foreach loop internally. By relying to those methods, we remove the need to iterate through the array on our end – that will be the job of extension methods.

Here is the modified function which utilizes LINQ to Objects:

static int CalculateControlDigit(UInt64 number)
{

    int[] digits = ToDigits(number);

    int sum =
        digits
        .Select((digit, index) => (1 + 2 * (index % 2)) * digit)
        .Sum();

    int controlDigit = sum % 11;
    controlDigit = (controlDigit  10) + (controlDigit % 10);

    return controlDigit;

}

From this implementation it becomes obvious why it was so important to remove the multiplicative factor variable and to calculate the factor in every iteration of the loop. In this implementation, we are using the Select extension method overload which tells us the index of each digit visited in the process. This lets us calculate the multiplicative factor in place.

This solution begins to look like functional. But it’s still not done. If you look at the function, it consists of several segments. The first segment splits the number into digits. The second segment calculates the sum. The third segment, which is the last one, calculates the control digit from the sum.

This sequence of steps looks procedural. We can modify the code to make it look more functional. As the first step, observe that ToDigits method can be implemented more elegantly:

static IEnumerable<int> DigitsFromLowest(UInt64 number)
{
    do
    {
        yield return (int)(number % 10);
        number = 10;
    }
    while (number > 0);
}

This almost trivial method returns a collection of digits of a given number, starting from the lowest digit. Now we can use this method to implement the CalculateControlDigit function without ever using the ToDigits function. On a related note, ToDigits function made an assumption about how many significant digits there are in the number. DigitsFromLowest, on the other hand, returns the collection of all digits no matter how many of them there are. Let’s put this new function into motion:

static int CalculateControlDigit(UInt64 number)
{

    int sum =
        DigitsFromLowest(number)
        .Reverse()
        .Select((digit, index) => (1 + 2 * (index % 2)) * digit)
        .Sum();

    int controlDigit = sum % 11;
    controlDigit = (controlDigit  10) + (controlDigit % 10);

    return controlDigit;

}

As you can see, the function has become even more compact than before. Note that we had to use the Reverse extension method to reverse order of digits. This is because sequence of multiplicative factors assumes that highest digit is taken first. We could, of course, start from the lowest digit and adapt the sequence of multiplicative factors to that case. But then, the function would be sensitive to total length of the insurance policy number. Suppose that 12-digit long policy numbers are added in the future – sequence of multiplicative factors starting from the least significant digit would then have to be reverted. In this way, with the Reverse method used, actual sequence of digits passed to the Select method is from the most significant digit towards least significant digit.

At this moment, the function that converts number to a collection of digits looks a little odd. It is defined in the same class that calculates the control digit, but it really doesn’t belong there. It would be better if the number itself could tell its digits. And that is possible, thanks to the extension methods. We can modify the code to work in the same way as any of the LINQ extension methods. Here is the class that accommodates an extension method to unsigned 64-bit integer numbers which lets them tell their digits out:

static class NumberExtensions
{
    public static IEnumerable<int> DigitsFromLowest(this UInt64 number)
    {
        do
        {
            yield return (int)(number % 10);
            number = 10;
        }
        while (number > 0);
    }
}

Right now, this class only defines this single extension method. But that is sufficient for us to simplify our control digit calculator another bit:

static int CalculateControlDigit(UInt64 number)
{

    int sum =
        number
        .DigitsFromLowest()
        .Reverse()
        .Select((digit, index) => (1 + 2 * (index % 2)) * digit)
        .Sum();

    int controlDigit = sum % 11;
    controlDigit = (controlDigit  10) + (controlDigit % 10);

    return controlDigit;

}

This time, the method is asking the number to split itself into digits. Then it sums up the digits according to rules. After this change, only the end of the function remains – part that calculates the control digit out of the sum. It doesn’t take much to turn this piece of code into functional code as well. Let’s define two more extension methods, this time for int data type:

static class NumberExtensions
{
    ...
    public static int Modulo(this int number, int divisor)
    {
        return number % divisor;
    }

    public static IEnumerable<int> DigitsFromLowest(this int number)
    {
        return ((UInt64)number).DigitsFromLowest();
    }

}

The first method adds operation Modulo to int data type. The second operation lets us split signed integer into digits. How do these two methods help us complete the control digit calculation? Here is the answer:

static int CalculateControlDigit(UInt64 number)
{
    return
        number
        .DigitsFromLowest()
        .Reverse()
        .Select((digit, index) => (1 + 2 * (index % 2)) * digit)
        .Sum()
        .Modulo(11)
        .DigitsFromLowest()
        .Sum();
}

This is almost final form of the control digit calculation, this time written as a functional program. It is possible to improve this code a bit by adding more convenient extension methods:

static class NumberExtensions
{
    ...
    public static IEnumerable<int> DigitsFromHighest(this UInt64 number)
    {
        return number.DigitsFromLowest().Reverse();
    }
    ...
    public static int SumDigits(this int number)
    {
        return number.DigitsFromLowest().Sum();
    }

}

Now the code becomes even easier to read:

static int CalculateControlDigit(UInt64 number)
{
    return
        number
        .DigitsFromHighest()
        .Select((digit, index) => (1 + 2 * (index % 2)) * digit)
        .Sum()
        .Modulo(11)
        .SumDigits();
}

There are two elements in this function that make it harder to understand. The first nuisance is the cryptic formula which calculates the sequence of multiplication factors 1, 3, 1, 3, etc. The other one is the last transformation which leaves all single-digit inputs intact, while input value 10 is transformed to output 1. These two transformations are purely arithmetic and they emerged directly from the requirements. But they do not read the same as requirements specified them. That could cause certain level of discomfort when reading this code. A little makeup could help make this first formula easier to read. Also, in both cases, a short comment could easily clarify the situation. This is the situation when programming language – C# in this case – doesn’t seem to be expressive enough to make these two transformations easier to read. Anyway, we can get away with a few words of comments, and a little bit of refactoring:

static int CalculateControlDigit(UInt64 number)
{
    int factor = 3;
    return
        number
        .DigitsFromHighest()
        .Select(digit =>
            {
                factor = 4 - factor;
                return factor * digit;  // factor = 1, 3, 1, 3...
            })
        .Sum()
        .Modulo(11)
        .SumDigits();
}

This implementation is based on a closure, which is another functional term. Variable factor is captured from the enclosing scope and used inside the delegate passed to the Select method. Note that this anonymous delegate is changing the value of the factor in every call.

Last statement can be made a little easier to understand by adding another specialized extension method:

static class NumberExtensions
{
    ...
    public static int ChangeIfEqualTo(this int number, int equalTo, int setTo)
    {
        if (number == equalTo)
            return setTo;
        return number;
    }

}
...
static int CalculateControlDigit(UInt64 number)
{
    int factor = 3;
    return
        number
        .DigitsFromHighest()
        .Select(digit =>
            {
                factor = 4 - factor;
                return factor * digit;  // factor = 1, 3, 1, 3...
            })
        .Sum()
        .Modulo(11)
        .ChangeIfEqualTo(10, 1);
}

This last step may be a little stretched. Personally, I would prefer to leave the previous implementation that sums up the digits, and just add a short comment beside it. Anyway, the code now reads the same as requirements in plain English, to the letter. Let me demonstrate it. Here is what we get if we try to read this method literally.

To calculate control digit of a number take digits of that number, starting from the most significant digit, multiply every other digit by factor 3, sum up the results, and take modulo 11 of the result. That will be the control digit, unless it is equal to 10. In this case, use value 1 instead.

This paragraph sublimates a very important aspect of functional programming. The code says what it does. There are no mental mappings to if-then-else statements or for loops. The most important quality of this way of coding is readability. We can read this function like a sentence.

Next thing that I plan to show you in this article is how to make value out of this function. Function itself is nice, but it has a limited value. Real value comes with reusability, and in the world of object-oriented programming, reuse comes with objects. Therefore, the time has come to put this function into a real class.

Combining Functional and Object-Oriented Programming

Now that we have a nice function for calculating the control digit for an insurance policy number, we can wrap that function into a nice class:

class PolicyNumber
{

    public UInt64 Value { get; private set; }

    private PolicyNumber(UInt64 number)
    {
        if (!ValidateNumberWithControlDigit(number))
            throw new System.ArgumentException("Control digit validation failed.");
        this.Value = number;
    }

    public static PolicyNumber FromNumberWithoutControlDigit (UInt64 number)
    {
        int controlDigit = CalculateControlDigit(number);
        return FromNumberAndControlDigit(number, controlDigit);
    }

    public static PolicyNumber FromNumberAndControlDigit(UInt64 number, int controlDigit)
    {
        return FromNumberWithControlDigit(10 * number + (UInt64)controlDigit);
    }

    public static PolicyNumber FromNumberWithControlDigit(UInt64 number)
    {
        return new PolicyNumber(number);
    }

    private static int CalculateControlDigit(UInt64 number)
    {
        int factor = 3;
        return
            number
            .DigitsFromHighest()
            .Select(digit =>
            {
                factor = 4 - factor;
                return factor * digit;  // factor = 1, 3, 1, 3...
            })
            .Sum()
            .Modulo(11)
            .ChangeIfEqualTo(10, 1);
    }

    private static bool ValidateNumberWithControlDigit(UInt64 number)
    {

        int controlDigit = (int)(number % 10);
        UInt64 rawNumber = number  10;

        int correctControlDigit = CalculateControlDigit(rawNumber);

        return controlDigit == correctControlDigit;

    }
}

This class is only used to contain actual policy number. Class design follows the rule that complete validation is done in the constructor. Once constructor execution ends and new object is created, the object is guaranteed to be correct. In particular, it means that control digit is correct. This rule has one important consequence in practical coding. We don’t have to ask whether any PolicyNumber instance is valid. If the object exists, it is valid. It is as hard as that.

Note that the only constructor of this class is declared private. Instances can only be created through one of the three static methods – these are the factory methods. Each of them can create a policy number under different circumstances. The first one receives a number without control digit. This method calculates control digit and attaches it to the number. Note that this particular method will never fail because it doesn’t have any control digit to validate. The second factory method receives a number and a control digit. The last factory method receives a complete number, which includes the control digit. These three methods let us construct policy number in any situation we might find ourselves in.

One more quality of the PolicyNumber is that the underlying number cannot change once the object has been constructed. PolicyNumber is an immutable class. It implements the Value Object design pattern, if you like it that way. To emphasize this fact, but also to protect ourselves from unintentional changes made to underlying policy number, we could let the compiler help us keep the object immutable:

class PolicyNumber
{

    private readonly UInt64 value;

    public UInt64 Value { get { return value; } }
    ...
}

In this way, underlying value must be set through the constructor. After that point there is no way to change it during the lifetime of the object. In order to change the policy number, we must construct a new object with the new underlying value.

But this is not all. Suppose that we have a class that represents insurance policy. Policy must be given a number at some point. Let’s simplify matters and say that the number is assigned when policy is instantiated:

class Policy
{
    public Policy(PolicyNumber number)
    {
        Console.WriteLine("Issuing a policy: {0:0000000000}", number.Value);
    }
}

We don’t need anything more than this at the moment. Now we want to be able to issue several insurance policies in a row, but in such a way that they occupy subsequent numbers. In order to support that, we could introduce another factory method which creates policy number which comes just after a given policy number:

class PolicyNumber
{
    ...
    public static PolicyNumber FromPreceding(PolicyNumber number)
    {
        UInt64 withoutControlDigit = number.value  10;
        UInt64 followerWithoutControlDigit = withoutControlDigit + 1;
        return PolicyNumber.FromNumberWithoutControlDigit(followerWithoutControlDigit);
    }
    ...
}

Now we can build a sequence of policy numbers:

class PolicyNumber
{
    ...
    public static IEnumerable<PolicyNumber> Sequence()
    {
        PolicyNumber current = PolicyNumber.FromNumberWithoutControlDigit(0);
        while (true)
        {
            current = PolicyNumber.FromPreceding(current);
            yield return current;
        }
    }
    ...
}

This way of combining objects into a sequence is sometimes funny. But at its core, it is functional. The Sequence method produces an infinite sequence of insurance policy numbers. Do you know how we can issue ten insurance policies in one batch now? Here it is:

Policy[] policies =
    PolicyNumber.Sequence()
    .Take(10)
    .Select(number => new Policy(number))
    .ToArray();

This statement builds a batch of ten insurance policies. Remember that policy class used to print assigned number when instantiated. Here is what we get on the console when this statement is executed:

            
Issuing a policy: 0000000011
Issuing a policy: 0000000022
Issuing a policy: 0000000033
Issuing a policy: 0000000044
Issuing a policy: 0000000055
Issuing a policy: 0000000066
Issuing a policy: 0000000077
Issuing a policy: 0000000088
Issuing a policy: 0000000099
Issuing a policy: 0000000101
                
    

Or, alternatively, suppose that we want to persist the last issued policy number and then to continue from the following number tomorrow. Here is the overloaded Sequence method which supports starting from a specific policy number:

class PolicyNumber
{
    ...
    public static IEnumerable<PolicyNumber> Sequence()
    {
        return Sequence(PolicyNumber.FromNumberWithoutControlDigit(0));
    }

    public static IEnumerable<PolicyNumber> Sequence(PolicyNumber lastIssued)
    {
        PolicyNumber current = lastIssued;
        while (true)
        {
            current = PolicyNumber.FromPreceding(current);
            yield return current;
        }
    }
    ...
}

Now we can issue a batch of ten policies with numbers that are following the last one issued before:

Policy[] policies =
    PolicyNumber.Sequence(PolicyNumber.FromNumberWithoutControlDigit(293742814UL))
    .Take(10)
    .Select(number => new Policy(number))
    .ToArray();

This time, the sequence of policies looks a bit differently:

            
Issuing a policy: 2937428152
Issuing a policy: 2937428163
Issuing a policy: 2937428174
Issuing a policy: 2937428185
Issuing a policy: 2937428196
Issuing a policy: 2937428200
Issuing a policy: 2937428211
Issuing a policy: 2937428222
Issuing a policy: 2937428233
Issuing a policy: 2937428244
                
    

This is the functional way of thinking about policy numbers. Policy number now behaves like any other number in the world – it only has a bit of validation that is intrinsic to it and it carries that logic with it wherever it goes.

Summary

In this article we have demonstrated that functions, although coded in object-oriented language such C# definitely is, are nothing more than data-driven procedures. We want to step out of the procedural mentality and to produce code which is fully object-oriented. But it is not an easy task to do.

Functional extensions to C# are an easy and elegant way to turn everything into objects. Any operation can be implemented by asking several objects to transform certain state. As long as objects are providing convenient functions that can transform their state into another state, we are happy to chain such operations into a sequence of transformations that lead us to the final result. When code is formatted in this way, it can be read as a sentence in natural language.

Another benefit from functional programming is that control constructs, such as loops and branching, can be entirely removed. These constructs are embedded in utility methods, such as LINQ extension methods that are applying a transformation to every element in the collection. Once this paradigm is fully accepted, a totally new way of thinking about code begins to emerge. It is no longer important to understand precise sequence of steps to perform an operation. That would return us back to procedural thinking. Instead, it becomes important to understand sequence of transformations applied to elements of a collection. These transformations can change input data into a single value as desired.


If you wish to learn more, please watch my latest video courses

About

Zoran Horvat

Zoran Horvat is the Principal Consultant at Coding Helmet, speaker and author of 100+ articles, and independent trainer on .NET technology stack. He can often be found speaking at conferences and user groups, promoting object-oriented and functional development style and clean coding practices and techniques that improve longevity of complex business applications.

  1. Pluralsight
  2. Udemy
  3. Twitter
  4. YouTube
  5. LinkedIn
  6. GitHub