Functional approach
Can improve:
- Predictability
- Maintainability
- Testability
Taming side effects
Side effects make the code more difficult to test and reason about
We can reduce side effects by enforcing immutability
public class NoSideEffects {
// no setter => value cannot change
public int Result { get; }
constructor(int value)
{
Result = value;
}
public NoSideEffects Add(int value)
{
// return a new object to prevent side effects
return new NoSideEffects(Result + value);
}
}
Emphasizing expressions
Can simplify code by always returning a value
Allows piping by method chaining
Statements
Have side effects
string result;
if(number >= 0)
{
result = "positiv"
}
else
{
result = "negative"
}
Expressions
Always return a value and have not side effects
Expressions can be called once or any number of times and they will produce the same result, for a given input.
Expressions are easier to test
var result = number >= 0 ? "positiv" : "negative"
Expression Methods
public static partial class FuncExtensions
{
public static TResult Map<T, TResult>(this T value, Func<T, TResult> function) =>
function(value);
}
public static partial class ActionExtensions
{
public static void Do<T>(this T value, Action<T> function) =>
function(value);
}
internal static void Calculate()
{
"-2".Map(int.Parse) // string -> int
.Map(Math.Abs) // int -> int
.Map(Convert.ToDouble) // int -> double
.Map(Math.Sqrt) // double -> double
.Do(Console.WriteLine); // double -> void
// Equivalent to:
Console.WriteLine(Math.Sqrt(Convert.ToDouble(Math.Abs(int.Parse("-2")))));
// or
string input = "-2.0";
int output1 = int.Parse(input); // string -> int
int output2 = Math.Abs(output1); // int -> int
double output3 = Convert.ToDouble(output2); // int -> double
double output4 = Math.Sqrt(output3); // double -> double
Console.WriteLine(output4); // double -> void
}