Click here to Skip to main content
14,355,411 members

Functional Programming In A Nutshell In C#

Rate this:
4.76 (5 votes)
Please Sign up or sign in to vote.
4.76 (5 votes)
24 Jul 2019CPOL
This article discusses functional programming in a nutshell in C#.

Table of Contents

  1. Introduction
  2. Functional Programming Definition
  3. Function Properties
    1. Purity
    2. First-Class Functions
    3. The Notion of Closure
    4. To Be Functional
  4. Functional Utilities
    1. Purity Consequences
    2. First-Class Consequences
  5. Functional Programming and Object-Oriented Programming
  6. Integrate Functional Programming
  7. Summing Up
  8. History

Introduction

Nowadays, functional programming is in fashion. There are two questions that we should ask ourselves:

  • Why this change happened from programmers and language creators?
  • And how to justify it?

There are a lot of functional languages:

  • C#, C++, Java
  • Lisp, JavaScript, Python, Smalltalk, Ruby, OCaml, F#, Scala, Groovy, D, Erlang, Clojure, Go, Swift, etc.

We can also notice that there are a lot of books about functional programming:

Image 1

There are two other important questions:

  • How to define a functional language?
  • And what is functional programming?

Functional Programming Definition

Functional programming is a programming based on functions, their compositions, and also based on decomposition of functions.

This effect of composition/decomposition is a sign of a programming paradigm (a way of modeling and building solutions).

All the languages cited as examples have the notion of function. Functional programming consists of using functions with particular properties.

Function Properties

There are two possible properties for functions:

  • Purity: Functions have results that do depend on their arguments without any other external effect.
  • First-class: Functions have a status of value.

Functional programming consists of exploiting one or two of these properties.

A functional language is a language that lets and favorizes functional programming.

Purity

Image 2

If a function is pure, that means that it will generate results that only depend on the arguments of the function without any other external effect.

Purity of functions:

  • Reject side effects and state
  • Advocate immutability of data structures

For instance, below is a pure function:

int f(int i){
  return i + 4;
}

f(1); // -> 5
f(1); // -> 5

And below is an impure function:

int j = 4;

int g(int i){
  j = i + j;
  return j;
}

g(1); // -> 5
g(1); // -> 6

As other examples, Log is a pure function. Random is an impure function.

First-Class Functions

Image 3

To be a first-class function means that the function has the same state as a value such as an integer or a character:

  1. A function can be named, affected and typed.
  2. A function can be defined and created on demand.
  3. A function can be passed as the argument of another function.
  4. A function can be returned from another function.
  5. A function can be stored in a data structure.

Given below are some examples:

  1. The ability to be named, affected and typed:
    Func<double, double> f = Math.Sin;
  2. The ability to be defined and created on demand:
    Func<int, int> f = x => x + 1;
  3. The ability to be passed as an argument to a function:
    g(f, 1, 2);

    where g is defined as follows:

    double g(Func<double, double> f, double x, double d){
      return f(x) + d;
    }
  4. The ability to be as a result of a function:
    Func<double, double> f = g(10);

    where g is defined as follows:

    Func<double, double> g(double x){
      return y => x + y;
    }
  5. The ability to be stored in a data structure such as a list:
    var l = new List<Func<double, double>> { Math.Sin, Math.Cos, Math.Tan };

Furthermore, C# allows lambda expressions to be represented as data structures called expression trees:

Expression<Func<int, int>> expression = x => x + 1;
var d = expression.Compile();
d.Invoke(2);

As such, they may be stored and transmitted.

The Notion Of Closure

Image 4

Closure and first-class are two independent properties. However, to be a pure and/or first-class, a function has to be transformed into a closure.

For instance, the following function is transformed to a pure function in another context when we associate to it the following environment:

int i = 0, j = 1;

int f(int k){
  return k + i + j;
}

The sign of a functional language is to transform automatically the definitions of functions into closures.

To Be Functional

The previous definitions indicate that:

  • Purity is a discipline of writing functions.
  • The five characteristics of first-class do not have to be all fulfilled to enhance the state of functions.
  • A functional language does not need to be functional exclusively.
  • Purity and first-class can be favored in languages in which they were not initially.

Functional Utilities

Image 5

What are the practical consequences of purity and first-class functions in term of development and programs?

Purity Consequences

Purity induces the following principal characteristics: Independency from the application context of a function.

Every sub-expression can be valued whenever and replaced by its result whenever. This implies the stability of composition of functions.

Given below are the consequences of purity:

  1. Formalities facilitated by the notion of function
  2. More complete and representative typing of behavior of functions
  3. Natural parallelization
  4. Improved readability and maintainability
  5. Easy tests
  6. Memorizing values (caching)
  7. Control of the evaluation

However, purity makes some things complicated:

  1. The management of data structures
  2. Explicit memory management
  3. The definition of inputs/outputs and error handling

It is possible to code only with pure functions. Haskell is a language that imposes that.

C# allows to annotate functions to indicate explicitly that they are pure:

[Pure]
bool f(int i){
  return i + 4 > 0;
}

Contract.Requires(f(0));

And in the official documentation of C#: "All methods called in a contract must be pure; in other words, they must not update a pre-existing state."

First-Class Consequences

First-class functions are functions that:

  1. Can be named, affected and typed
  2. Can be defined and created on demand
  3. Can be passed as argument to functions
  4. Can be the result of a function
  5. Can be stored in a data structure

If a function can be passed as argument to a function, it will imply the possibility of generalization/functional abstraction. Every portion of code in a function can be replaced by an abstraction (function call).

A simple code:

float M(int y){
  int x1 = [ ... ];
  int x2 = [ ... ];
  [ ... ]
  [ ... some code ... ]; // some code using x1, x2 and y
  [ ... ]
}

with functional abstraction:

public delegate int Fun(int x, int y, int z);

float MFun(Fun f, int x2, int y){
  int x1 = [ ... ];
  [ ... ]
  f(x1, x2, y);
  [ ... ]
}

int z1 = MFun(F1, 1, 2);
int z2 = MFun(F2, 1, 2);

The advantages of functional abstraction is that there are no local duplications and there is separation of concerns.

A simple and effective application of the functional abstraction is generic higher-order iterated operations over data.

For instance, the internal iterators (Maps):

IEnumerable<T2> Map<T1, T2>(this IEnumerable<T1> data, Func<T1, T2> f){
  foreach(var x in data)
    yield return f(x);
}

someList.Map(i => i * i);

As another consequence of first-class, we have the definition of generic function types. C# offers functional and procedural generic delegate predefined types for arity up to 16:

delegate TResult Func<TResult >();
delegate TResult Func<T, TResult>(T a1);
delegate TResult Func<T1, T2, TResult>(T1 a1, T2 a2);
delegate void Action<T>(T a1);
[ ... ]

First-class functions can all be lightened by inference-type. C# provides var keyword, but it is too weak for function types.

To be defined and created on demand, is done through anonymous functions, also called lambdas:

delegate(string s) { return s + "some string"; };

Anonymous delegates can look even more like lambda expressions:

(s => { return s + "some string"; });
s => s + "some string";

A principal example is the generalization of iterative treatments on data structures with three types of functions called: maps, reduces/folds and filters.

For instance, the internal iterators (Maps):

IEnumerable<T2> Map<T1, T2>(this IEnumerable<T1> data, Func<T1, T2> f){
  foreach (var x in data)
    yield return f(x);
}

someList.Map(i => i * i);

Most of functional languages provide maps/reduces/filters in their libraries (Select/Aggregate/Where in C#).

The maps/reduces/filters function sets are also used to state the compositions of treatments:

LINQ for instance:

var q = programmers
.Where(p => p.Age > 20)
.OrderByDescending(p => p.Age)
.GroupBy(p => p.Language)
.Select(g => new{ Language = g.Key, Size = g.Count(), Names = g });

To be as a result of a function, allows:

  • Adaptations of functions
  • Partial applications (currying)

With first-class functions, every n-ary function can be transformed into a composition of n unary functions, that is, into a curried function:

Func<int, int, int> lam1 = (x, y) => x + y;
Func<int, Func<int, int>> lam2 = x => (y => x + y);
Func<int, int> lam3 = lam2(3); // partial application

Currying:

public static Func<T1, Func<T2, TRes>> Curry<T1, T2, TRes>(this Func<T1, T2, TRes> f){
  return (x => (y => f(x, y)));
}

Func<int, int> lam4 = lam1.Curry()(3); // partial application

To be stored in a data structure allows:

  • Dynamic modularity: Management of function sets, structured lists, tables, trees, graphs, etc.
  • Passing arguments as function sets (instead of simple functions)

Also, one of the consequences is data-driven programming.

Some functional programming techniques use purity and first-class:

  • Mapreduce (Select + Aggregate in C#): generalized iterative treatments pure functions for easy parallelization
  • Control of evaluation by functional emulation: pure expressions encapsulated in functions without parameters (On-demand call-by-call evaluation of these functions)

Functional Programming And Object-Oriented Programming

Image 6

Why is functional programming usually integrated to object-oriented programming?

Principal object-oriented programming languages are based on classes as modules: C#, C++, Java.

One of the strong ideas of development in object-oriented programming: maintenance, extension and adaptation actions can go through inheritance and class composition (This avoids any modification of the existing code). Functional programming is a solution to this problem.

For example, the Strategy design pattern.

A Strategy pattern is to let an algorithm vary independently of clients that use it:

Image 7

A Strategy: just a case of abstracting code at a method level (No need for object oriented encapsulation and new class hierarchies). For instance, in the .NET Framework:

public delegate int Comparison<T>(T x, T y);
public void Sort(Comparison<T> comparison);

public delegate bool Predicate<T>(T obj);
public List<T> FindAll(Predicate<T> match);

Image 8

Other design patterns such as Command, Observer, Visitor and Virtual Proxy can benefit first-class functions:

Image 9

You can find out more about that in my following article.

Integrate Functional Programming

A language that incorporates first-class functions should facilitate functional programming and promote it.

Summing Up

Functional programming is good for development: purity and first-class induce a degree of stability, determinism, testability, partitioning, fluidity of use, compositionality, generalization, extensibility, etc.

  • Functional programming is good for modular-objects
  • Functional programming is soluble: purity and first-class functions can be considered, included and facilitated in any language
  • Code abstraction at a function/method level
  • Convenient generic iterator/loop implementations
  • Operation compositions, sequence/query comprehensions
  • Function partial applications
  • Limitations of the number of object/class definitions
  • Name abstractions at a function/method level
  • Architecture simplifications
  • Increased flexibility

History

  • 24th July, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Akram El Assas
Architect
Morocco Morocco
Akram El Assas graduated from the french engineering school ENSEIRB located in Bordeaux, a city in the south of France, and got his diploma in software engineering in 2010. He worked in France for Mediatvcom, a company specialized in audiovisual, digital television and new technologies. Mediatvcom offers services such as consulting, project management, audit and turnkey solutions adapted to the needs of customers. Akram worked mainly with Microsoft technologies such as C#, ASP.NET and SQL Server but also with JavaScript, jQuery, HTML5 and CSS3. Akram worked on different projects around digital medias such as Media Asset Management systems, Digital Asset Management systems and sometimes on HbbTV apps.

Comments and Discussions

 
QuestionQuestion ... obviously Pin
Michael Breeden30-Jul-19 6:07
memberMichael Breeden30-Jul-19 6:07 
PraiseGood info... Pin
Member 85002330-Jul-19 4:19
memberMember 85002330-Jul-19 4:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 24 Jul 2019

Tagged as

Stats

4.7K views
69 downloads
6 bookmarked