This article discusses functional programming in a nutshell in C#.

Please Sign up or sign in to vote.

Functional Programming In A Nutshell In C#

Table of Contents

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:

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.

: 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

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 ); f( 1 );

And below is an impure function:

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

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

First-Class Functions

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

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

Given below are some examples:

The ability to be named, affected and typed: Func<double, double> f = Math.Sin; The ability to be defined and created on demand: Func<int, int> f = x = > x + 1 ; 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; } 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; } 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

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

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:

Formalities facilitated by the notion of function More complete and representative typing of behavior of functions Natural parallelization Improved readability and maintainability Easy tests Memorizing values (caching) Control of the evaluation

However, purity makes some things complicated:

The management of data structures Explicit memory management 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:

Can be named, affected and typed Can be defined and created on demand Can be passed as argument to functions Can be the result of a function 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 ... ]; [ ... ] }

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 ( Map s):

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 );

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 );

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

+ 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

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:

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);

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

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