Click here to Skip to main content
15,884,298 members
Articles / Desktop Programming / MFC

Functional Programming in a Nutshell

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
11 Feb 2013CPOL8 min read 11.7K   6  
Functional programming is a programming paradigm that treats application operations as a sequential execution of functions.

Introduction

Functional programming is a programming paradigm that treats application operations as a sequential execution of functions. Basically, your code becomes a huge math operation. But wait, is not as bad as it sounds. In C#, this is achieved by using delegate functions, lambda expressions and LINQ.

For example, consider the following code:

void Main()
{
    int result = Sum(1, 2);

    Console.WriteLine("1 + 2 = " + result);
}

Basically, the previous code simply performs an addition of a couple of numbers and then displays the result in the application console. By using functional programming, we could rewrite the previous code as follows:

public void Main() 
{
    var result = () => 1 + 2;
    
    Console.WriteLine("1 + 2 = " + result.ToString());
}

Now, the addition result is assigned by executing an inline function. The way it is written (the arrow thingy) is called lambda notation.

Delegate Functions

Functional programming in C# has its roots in the concept of delegate functions, which are functions treated like regular variables and are to be used at any time the instantiator pleases, delegating a particular task to it. In other words, delegates are variables that instead of containing a value, it contains an executable method. The instantiator of the delegate can use it in all the ways a variable can be used, even passed as a method argument.

Since delegates are like regular variables, to create a delegate function we first have to declare its type. A delegate type declaration, however, is nothing like a regular class declaration. Is more like an interface method declaration; it defines the arguments and return type:

delegate int Sum(int left, int right);

Here, we declare a delegate type called Sum which takes two integer arguments and returns another integer, representing the addition result. After we declare the delegate type, we can start creating instances of it.

delegate int Sum(int left, int right);
    
void Main() 
{
    Sum sum = delegate(int left, int right) 
    {
        return left + right;
    };
}

We just created a delegate function of type Sum, which performs the integer addition and returns the resulting value. The real magic can be seen when you are able to use this delegate as a variable and call it as a method whenever necessary.

delegate int Sum(int left, int right);
    
void Main() 
{
    Sum sum = delegate(int left, int right) 
    {
        return left + right;
    };

    int total = 0;
    int[] numbers = { 1, 2, 3 };

    foreach(int number in numbers) 
    {
        total = sum(total, number);
    }
}

As mentioned before, a delegate can be used in any way a traditional type can be used: variables, method parameters, properties and class-level fields. This represents a huge advantage, in terms of encapsulation. Methods can take a delegate type parameter and execute it without knowing what is really going on inside of it, more like a black box. Since the method knows the return type and arguments, it should not care about its inner workings.

Consider the following piece of code:

delegate int Sum(int left, int right);

class MathConsole
{
    static void ShowSumInConsole(Sum sum, int left, int right)
    {
        int result = sum(left, right);
        
        Console.WriteLine("Result: " + result.ToString());
    }
}

In the previous example, the caller of the ShowSumInConsole function must supply a delegate function as the sum argument that will be in charge of performing the number addition, regardless of how it does it; and return the resulting integer. At some point, the ShowSumInConsole will call it when the resulting integer is required.

In a way, this allows the caller to control how the number addition is to be performed. This is what we know as the principle of Inversion Of Control; or IoC for the folks. Basically, is a technique that allows the caller to take control of a particular operation executed in another function being called.

Sum sumWithTrace = delegate(int left, int right)
{
    Trace.WriteLine("Adding " + left + " to " + right);
    
    return left + right;
};

MathConsole.ShowSumInConsole(sumWithTrace, 2, 3);

Like aforementioned, we are assuming control of how to do the number addition; the ShowSumInConsole is left only with the task to do exactly what its name says: show the addition result in the application console.

Another important thing to notice is that the ShowSumInConsole does not know where the Sum delegate function comes from or what it does to return the resulting integer, hence, it is known as an anonymous function. JavaScript developers can start to relate here.

Built-in Delegate Types

Previously, we created a custom type for our Sum delegate. However, the .NET Framework provides several built-in delegate types for pretty much every of your needs. These are located in the System namespace and the most commonly used are the Action and the Func delegate types.

Action Delegate Type

The Action delegate type represents a delegate with no return value, or as we commonly know it: a void method. The signature looks as follows:

Action<in T> // A delegate with only one argument.

Action<in T1.. in TN> // A delegate with one or N arguments.

Consider the following delegate:

delegate void SendDataAction(string url, string data);

void Main() 
{
    SendDataAction sendData = delegate(string url, string data) 
    {
        // Send data to supplied URL...
    };
    
    sendData("http://example.com", "Hello world!");
}

By using the Action delegate type, we can rewrite the previous example as follows:

void Main() 
{
    Action<string, string> sendData = delegate(string url, string data) 
    {
        // Send data to supplied URL...
    };
    
    sendData("http://example.com", "Hello world!");
}

As you could have figured out already, Action<string, /> is a delegate type for an inline method that takes two arguments of type string. We can strip down the example even more by using lambda notation:

void Main() 
{
    Action<string, string> sendData = (url, data) =>
    {
        // Send data to supplied URL...
    };
    
    sendData("http://example.com", "Hello world!");
}

Now, things start to get interesting as we dive into the twisted dimension of lambda expressions. Here we don't have to specify the typed of the delegate arguments since we already did that when we specified what overload of the Action delegate type we were gonna use, in this case an Action<string, string>, or an Action delegate with two string arguments.

Function Delegate Type

A function delegate represents a delegate with return value and, optionally, one or more arguments. For these, we use the Func delegate type, which signature looks as follows:

Func<out TResult> // A delegate with no arguments.

Func<in T1.. in TN, out TResult> // A delegate with N arguments.

In this particular type, the first type parameter, out T, corresponds to the delegate return type. The rest of the type parameters represent each argument respectively.

Now, consider the following delegate:

delegate string GetDataFunction(string url);

void Main() 
{
    GetDataFunction getData = delegate(string url) 
    {
        // Perform request to URL to get the data.
        
        return data;
    };
    
    var data = getData("http://example.com");
    
    Console.WriteLine("Returned data: " + data);
}

This time, we create a delegate that performs a request to the specified URL and then returns the response data to the caller as string. We can, however, rewrite the code to use the Func type as follows:

void Main() 
{
    Func<string, string> getData = delegate(string url) 
    {
        // Perform request to URL to get the data.
        
        return data;
    };
    
    var data = getData("http://example.com");
    
    Console.WriteLine("Returned data: " + data);
}

Or, in its simplest form, as a lambda expression:

void Main() 
{
    Func<string, string> getData = (url) =>
    {
        // Perform request to URL to get the data.
        
        return data;
    };
    
    var data = getData("http://example.com");
    
    Console.WriteLine("Returned data: " + data);
}

Again, when writing the delegate in lambda notation, we don't have to specify the types of the delegate arguments, we already did that by using a Func<string, string> delegate type; or a Func delegate with a single string argument and a string return type. Remember, the last type parameter always specifies the delegate return type.

Methods As Delegates

We already mentioned that some delegates are inline functions directly declared inside a method and such, but is important to know that method declarations in classes can be also delegates. For example, consider the following code:

class CarFactory 
{
    Car CreateCar(Func<Engine, string> engineFactory)
    {
        var engine = engineFactory("V8");
        var car = new Car(engine);
        
        return car;
    }
}

In this particular case, the CreateCar method takes a delegate as argument. This delegate will return a car engine instance for the specified engine name. We use a Func<Engine, string> since it is expected to return an instance of the Engine class and take a string as argument, representing the name of the engine to create. Now, consider the following code, which makes use of the CarFactory class:

void Main() 
{
    CarFactory carFactory = new CarFactory();
    Car car = carFactory.CreateCar(this.CreateEngine);
    
    Console.WriteLine("Car brand: " + car.Brand);
}

Engine CreateEngine(string name) 
{
    if (name == "V8") 
    {
        return new V8Engine();
    }
    else
    {
        throw new NotSupportedException();
    }
}

As we can observe in this code, the CreateEngine method is passed to the CreateCar method in the CarFactory class instance as a delegate of type Func<Engine, string>. The CreateCar method can use it, even if it belongs to the caller instance. So, the only rule we have to consider when using a method as a delegate is that the method have to comply with the delegate type parameters in order. In this case, CreateCar requires a delegate with a return type of Engine and a single argument of type string. The CreateEngine method matches this requirements and can be effectively passed to the CreateCar method so it uses it at its own will. Now we're sharing members between classes.

Lambda Expressions

In C#, a lambda expression is an inline function declared on-the-run and used as a regular variable. An example of a lambda expression is as follows:

void Main() 
{
    CarFactory carFactory = new CarFactory();
    Car car = carFactory.CreateCar((name) => 
    {
        if (name == "V8") 
        {
            return new V8Engine();
        }
        else
        {
            throw new NotSupportedException();
        }
    });
    
    Console.WriteLine("Car brand: " + car.Brand);
}

Now, we don't declare methods to be used as delegates, we now write a lambda expression representing the delegate body to be executed by the CreateCar method in an inline manner. This is known as lambda notation and is commonly used in LINQ, or Language Integrated Query, when using its chained methods form.

LINQ

LINQ is a feature of the C# language that allows the developer to write SQL-like queries to filter and retrieve data from a collection. For example:

void Main() 
{
    string[] names = { "John", "Douglas", "Albert" };
    string[] smallNames = names.Where(name => name.Lengh <= 4).ToArray();
}

In the previous example, we use the Where method which, internally, iterates through the collection and calls the supplied Func<string, bool> delegate which is going to be supplied of the current name being evaluated and it is expected to return a boolean value indicating whether it satisfies the condition or not. Then, the resulting elements are converted to an array by using the ToArray method, which is also part of LINQ.

On the other hand, it would be the same as writing the following code the old way:

void Main() 
{
    string[] names = { "John", "Douglas", "Albert" };
    string[] smallNames = FilterNames(names, 4).ToArray();
}

IEnumerable<string> FilterNames(string[] names, int maxLength) 
{
    foreach(string name in names)
    {
        if (name.Length <= maxLength)
        {
            yield return name;
        }
    }
}

You might have noticed one of the main advantages of LINQ and delegates in general: It saves a whole bunch lines of code. Another advantage is that we keep the code simple and easy to read.

LINQ has several methods that extends the IEnumerable interface, which is the interface implemented by every single collection type in the .NET Framework, such as arrays, lists and dictionaries. Some of these methods are:

  • Where: Filters the source collection returning only the elements satifying the specified expression.

                // Return all names starting with the letter "L".<br />
    names.Where(name => name.StartsWith("L"));             

  • OrderBy: Orders the source collection using a sort expression.

                // Orders the persons by their first name.<br />
    persons.OrderBy(person => person.FirstName);              

  • Take: Gets a specified number of items from the source collection.

    // Gets 10 cars.<br />
    cars.Take(10);
    

  • Skip: Skips a specified number of items in the source collection.

                // Skips the first 10 cars and take the next 10.<br />
    cars.Skip(10).Take(10);

  • Any: Returns a boolean value indicating whether any element in the source collection meets the specified expression.

    // Determine whether any account has SSN equal to 600-521-456. <br /> accounts.Any(account => account.SSN == "600-521-456");

These are some examples of LINQ methods. To use these, you have to include the System.Linq namespace. Some other methods to consider are the collection conversion methods which are:

  • ToArray: Converts the source collection to an array.
  • ToList: Converts the source collection to a strongly-typed list.

Conclusion

These are the basics of functional programming in C#. Basically, it comes to make a more readable code, which provides several benefits like code maintanability and ease of future development. Some topics are left out, such as predicate expressions and expression trees. But these will be covered in a future article.

License

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


Written By
Software Developer (Senior) Informatech
Costa Rica Costa Rica
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --