Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Functional C#: An imperative to declarative example

DZone's Guide to

Functional C#: An imperative to declarative example

·
Free Resource

I wrote previously about how we've been working on some calculations on my current project and one thing we've been trying to do is write this code in a fairly declarative way.

Since we've been test driving the code it initially started off being quite imperative and looked a bit like this:

public class TheCalculator
{
...
public double CalculateFrom(UserData userData)
{
return Calculation1(userData) + Calculation2(userData) + Calculation3(userData);
}

public double Calculation1(UserData userData)
{
// do calculation stuff here
}

public double Calculation2(UserData userData)
{
// do calculation stuff here
}
...
}

What we have on line 7 is a series of calculations which we can put in a collection and then sum together:

public class TheCalculator
{
...
public double CalculateFrom(UserData userData)
{
var calculations = new Func<UserData, double>[] { Calculation1, Calculation2, Calculation3 };
 
return calculations.Sum(calculation => calculation(userData));
}
 
public double Calculation1(UserData userData)
{
// do calculation stuff here
}
...
}

We can pull out a 'Calculation' delegate to make that a bit more readable:

public class TheCalculator
{
private delegate double Calculation(UserData userData);

public double CalculateFrom(UserData userData)
{
var calculations = new Calculation[] { Calculation1, Calculation2, Calculation3 };

return calculations.Sum(calculation => calculation(userData));
}
...
}


One of the cool things about structuring the code like this is that if we want to add a new Calculation we can just go to the end of the array, type in the name of the method and then Resharper will create it for us with the proper signature.

We eventually came across some calculations which needed to be subtracted from the other ones, which seems like quite an imperative thing to do!

Luckily Christian saw a way to wrap these calculations in a 'Subtract' function so that we could stay in declarative land:

public class TheCalculator
{
private delegate double Calculation(UserData userData);

public double CalculateFrom(UserData userData)
{
var calculations = new [] { Calculation1, Calculation2, Calculation3, Subtract(Calculation4) };

return calculations.Sum(calculation => calculation(userData));
}
...
public Calculation Subtract(Calculation calculation)
{
return userData => calculation(userData) * -1;
}
}

Having a method which explicitly has the 'Calculation' signature allows us to remove it from the array declarative which is pretty neat.

We can also change the method signature of 'Subtract' to take in a variable number of calculations if we need to:

public class TheCalculator
{
...
public double CalculateFrom(UserData userData)
{
var calculations = new [] { Calculation1, Calculation2, Calculation3, Subtract(Calculation4, Calculation5) };

return calculations.Sum(calculation => calculation(userData));
}

public Calculation Subtract(params Calculation[] calculations)
{
return userData => calculations.Sum(calculation => calculation(userData)) * -1;
}
}

The other nice thing about coding it this way is that we ran into a problem where when we fed real data through the code we were getting the wrong values returned and we wanted to understand where it was falling down.

We could easily temporarily add in a 'Console.WriteLine' statement like this to help us out:

public class TheCalculator
{
...
public double CalculateFrom(UserData userData)
{
var calculations = new [] { Calculation1, Calculation2, Calculation3, Subtract(Calculation4, Calculation5) };

return calculations
.Select(calculation =>
{
Console.WriteLine(calculation.Method.Name + " = " + calculation(userData));
return calculation;
})
.Sum(calculation => calculation(userData));
}
...
}

It then printed the results down the page like so:

Calculation1: 23.34
Calculation2: 45.45
...

 

Topics:

Published at DZone with permission of Mark Needham, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}