Functional Programming in C#

Learn how to write better C# code

Expressions vs Statements

Every C# application consists of statements made up of keywords, expressions and operators.

An expression is anything that yields a value and can be used in places where a value is expected. For example:

  1. literal values as 12, "test"
  2. method invocation, such as Convert.ToInt32("12");
  3. operator and its operands, such as x || y, x == true ? x : y

The expression x + y (if both x and y are integers) evaluates to integer value, new SomeClass() evaluates to a reference to a new instance of a SomeClass object and someClass.ToString() to a string.

Statements are the actions that the program takes. They do not return any result. Common actions are assignments, if/else conditions, looping through collection and calling methods. The order in which statements are executed is called the flow of control and may vary every time that a program is run, depending on runtime input. Example of statements are:

int i; // declaration statement
i = 12; // assignment statement
// i + 1 is expression
int [] nums = { 1, 2, 3 };
// foreach statement block with nested selection statement
foreach (int num in nums)
{
if (num % 2 == 0)
{
}
}

One fundamental difference between imperative and functional style is that the first one relies heavily on statements to accomplish everything, while functional code relies on expressions. In most cases the expression is expected to evaluate to some value (with some possible side effects), while the sole purpose of statement is to have side effects.

In functional languages like F# or Haskell everything is an expression, and even statements are expressions.

Since statement doesn't return anything, it has to modify variable or change state. In the example below we want to calculate amount which may have a discount

public decimal CalculateAmountWithDiscount(decimal amount, bool hasDiscount)
{
decimal discount = 0;
if (hasDiscount)
{
discount = 10;
}

return amount – amount * discount / 100;
}

Notice that the only purpose of the if statement is to mutate the discount variable. Another issue with this approach is the discount variable, which has to be declared outside the statement. This means that the if statement cannot be easily refactored (for example extracted in another method) because it depends on a variable that is not part of the statement. For comparison, here is the same code, using expression:

public decimal CalculateAmountWithDiscount(decimal amount, bool hasDiscount)
{
decimal discount = hasDiscount ? 10 : 0;
return amount – amount * discount / 100;
}

In this version there is no mutation of variable, and also the code is more compact.

Some benefits of using expressions:

  1. Multiple expressions can be combined together into larger expressions, so if everything is expression, then everything is composable.
  2. Statements always have to be evaluated in order. With expressions, the subexpressions do not have implied order of execution.
  3. Expressions can be called once or any number of times and they will produce the same result, for a given input.
  4. Expressions are easier to test. In the expression a + b, if both a and b don't cause side effects, then a can be isolated and tested on its own, as can b. Expressions will not give a different result because you called it multiple times or at a different time.

Continue reading: Higher-Order functions

Resources: