Functional Programming Concepts Applied Using C#

There has been a lot of fuzz in recent years about functional programming. You might be interested but it can be hard to apply it to your everyday work. This post aims to give you a cheatsheet with functional concepts and techniques used in the .NET framework.

Arrow Notation

Arrow notations are the preferred syntax to read the signature of a function in Functional Programming lingo and they are right associative,

(int) -> ((int) -> (int)) EQUALS int -> int -> int

That can be read as a function that takes two integers as arguments and returns another integer. In C# we have three ways to create it,

Delegates = private delegate int ExampleDelegate(int left, int right) left and right are the first two ints in the signature and the return type is the third int.

Funcs = private Func <int, int, int> ExampleFunc it can be read as in the example, the first two ints are the arguments, and the last one, is the return type.

Actions = private Action<int, int, int> ExampleAction are a special case, they read as Funcs but they always return void. It is read as a function that takes 3 ints as arguments and returns void.

First Class Functions

When functions become first class citizens in a language. Means they can,

  • Be used as parameters in other functions
  • Be used as the return type of a function
  • They can be assigned to variables
  • Be stored in a collection

Higher Order Functions

Higher Order Functions or HOFs are functions that take other functions as inputs or return a function as output, or both.

Pure Functions

It is said that a function is pure when its output depends entirely on the input parameters. And cause no side effects. Therefore they improve,

  • Testability
  • Correctness
  • Readability

With no side effects there are no surprises. our code does what it says, no more. and the cognitive load to understand the code is less

Side Effects

To clarify this definition, we must define exactly what a side effect is. A function is said to have side effects if it does any of the following,

  • Mutates global state — “Global” here means any state that’s visible outside of the function’s scope. For example, a private instance field is considered global because it’s visible from all methods within the class.
  • Mutates its input arguments
  • Throws exceptions
  • Performs any I/O operation — This includes any interaction between the program and the external world, including reading from or writing to the console, the filesystem, or a database, and interacting with any process outside the application’s boundary.

Immutability

In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. In C# we can use this Nuget package to enforce it. And we can also follow some practices that I will show you next

Arity Of Functions

In logic, mathematics, and computer science, the arity of a function or operation is the number of arguments or operands that the function takes,

  • Nullary, takes no arguments
  • Unary, takes one argument
  • Binary, takes two arguments
  • Ternary, takes three arguments
  • n-ary, takes n arguments

Referential Transparency

In functional programming, referential transparency is generally defined as the fact that an expression, in a program, may be replaced by its value (or anything having the same value) without changing the result of the program. This implies that methods should always return the same value for a given argument (purity of functions), without having any other effect.

This functional programming concept also applies to imperative programming, though, and can help you make your code clearer.

Delegates

Delegates are the most basic form to use functions as first-class citizens in C#.

Actions

Actions are sugar syntax to create delegates that the .NET framework gives us. These never return a value therefore are always void, and can take up to sixteen generic parameters as input.

  • The void return type in FP is called unit
  • unit is expressed as () in arrow notation

Funcs

Funcs are another sugar syntax to create delegates that the .NET framework gives us. But these do have a generic return type, and can take up to sixteen generic parameters as input. The key difference between Actions and Funcs is the last parameter of a Func which is the return type.

Curried Functions

Named after mathematician Haskell Curry, currying is the process of transforming an n-ary function f that takes arguments t1, t2, …, tn into a unary function that takes t1 and yields a new function that takes t2, and so on. Ultimately returning the same result as f once the arguments have all been given. In other words, an n-ary function with signature

(T1, T2, …, Tn) → R

when curried has a signature

(T1) (T2) … (Tn) → R

By itself this technique is pretty useless, but it does enable the use of partial application.

Partial Application

Providing a function with fewer arguments than it expects is called Partial Application of functions. In order to produce more reusable functions and create better abstractions we need a mechanism to preset some of the arguments of a function. Partial application is the technique used to transform functions of higher arity (functions that take multiple arguments) into multiple functions that take less arguments.

Lazy Evaluation / Deferred Execution

Laziness in computing means deferring a computation until its result is needed. This is beneficial when the computation is expensive and its result may not be needed.

Extension Methods

Extension methods enable you to “add” methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

  • They are just static methods in a static class
  • Enable the Open/Close principle
  • Enable method chaining

Smart Constructors

Smart constructors are just functions that build values of the required type. Yet perform some extra checks when the value is constructed. In this case it is a good practice to set the constructor of a Reference Type as private and expose a function to allow its instantiation.

Avoid Primitive Obsession

Primitive obsession is the use of base types like: string, int, bool, double, datetime etc to represent complex types like a person’s Age, Name or Email.

To avoid this we should create small objects to represent them and some of the benefits we get from that are,

  • Validation in one single place (the small class smart constructor)
  • Immutability
  • Self-Validation
  • Value Equality

Generics

A generic allows you to define a class with placeholders for the type of its fields, methods, parameters, etc. Generics replace these placeholders with some specific type at compile time.

  • Used to maximize code reuse, type safety, and performance
  • Its most common use is to create collection classes
  • Constraints work to limit the scope of the types

LINQ

LINQ stands for Language INtegrated Query. It is a functional library and offers implementations for many common operations on lists or, more generally, on “sequences.” As instances of IEnumerable they should technically be called that way, the most common of which are mapping, sorting, and filtering.

  • Makes extensive use of higher-order functions as arguments
  • Uses method syntax or query syntax

Conclusion

I hope that this post has helped you to understand more about the Functional Programming paradigm and how can we use it in our projects, to improve readability, testability and correctness of our software. Remember that FP and OOP are orthogonal and you can use the best of both worlds.

If you have any feedback or questions contact me at [email protected].

Happy coding!

Focus Mode

Contact Request

Close

We will call you right away. All information is kept private