C++ => C# => F#, more functional, more parallel (1)
On DotNet Board of USTC BBS, orochi recommended a book named “Functional Programming For The Real World”. It demonstrates advantages of functional programming towards traditional imperative programming, which inspires me a lot. Here, I will first narrate my understanding of such advantages as a C# developer, then take an example to address how the programming pattern changes from pure imperative language as C++, to C# which contains a lot of functional programming elements, then to typical functional language F#.
As I mentioned in this blog before, functional programming is another programming paradigm besides imperative programming. It is based on lambda calculus, which is as descriptive as a Turing machine. Anyway, this statement demonstrates nothing valuable. Let see the differences between imperative programming and functional programming in detail:
1. Function is treated as the fundamental element in functional programming. In functional programming languages, functions can be treated as independent variables (instead of considered as a pointer in languages like C++), can be created and destroyed dynamically like other kinds of variables, and can have no name (i.e. anonymous). This is a difference, but I admit that I haven’t found any advantages caused by this point directly. L Maybe when finish the studying of F#, I can answer this question. :-)
2. Functional programs tell computer what to do rather than how to do it. For example, if we want to select all the costumers living in UK, we may write like this in imperative languages like C++:
1 for(vector<CCustomer>::const_iterator i = Customers.begin(); i != Customers.end(); ++i)
2 {
3 if (i->LivingIn == "UK")
4 {
5 UKCustomers.push_back(*i);
6 }
7 }
This code snippet tells the computer: first pick up a customer from the customer list, check whether he/she lives in UK, if so, copy it to a new list called UKCustomers, do noting otherwise, then pick up next and do the same thing, until all the customers are processed. We are quite familiar with such “algorithms”, huh? Let’s see how we tell the computer to do the same thing with C#:
1 var UKCustomer =
2 from customer in customers
3 where customer.LivingIn == "UK"
4 select customer;
What we need to do is just tell the computer: to pick up all the customers living in UK. Tell the computer what to do rather than how to do, this is called declarative style, which is used by popular languages such as SQL, as well as functional programming languages.
3. Variables in functional languages are immutable, i.e. they cannot be changed after created and initialized. A little advantage caused by this feature is, the codes is easier to understand and with less ambiguous. For example, without reading the manual, we don’t know what is the result of rectangle.Inflate(int width, int height). Will it inflate the original rectangle or return a new inflated rectangle? Both the choices are reasonable. However, in the functional world, only the second situation is possible. Of course, this style is not intended to save the “mass world” of imperative languages, but to make it possible that:
4. Write parallel code very easily. As every object in functional language is immutable, there will be no blocking, no lock or no writing collisions in the multi-thread world. Furthermore, it becomes very cheap to recognize which part of the program can be executed currently, so that the automatic optimization by the compiler is possible. That means, we need only make some tiny changes, then the program can run on multi-core computer or clusters with compiler’s optimizations. Take the sample code mentioned in the o_Ops! T-Shirt:
1 girls.Where(x => x.IsBeauty).All(x => { x.Hi(); return true; });
which means say hi to all the beautiful girls in a list named girls. If you are a mono-core computer, you have to select all the beautiful girls, then say hi to them one by one. It is very uncompetitive, isn’t it? So we can modify the program like this, with the help of Parallel Extensions of .NET, which you can download here:
1 girls.AsParallel().Where(x => x.IsBeauty).All(x => { x.Hi(); return true; });
Now you can say hi to all the beautiful girls simultaneously, with enough cores. :-) is it simple? No explicit processes or threads, no configuration for MPI or OpenMP. Thanks to the immutability of LINQ in C# 3.0, parallel programming turns so simple.
Besides easy parallel programming, immutability also makes lazy computing possible, which is implemented in C# LINQ and F#.
(to be continued… next part you will see how we accomplish part of a map rendering engine in C++, C# and F#, demonstrating how the functional programming paradigm makes parallel coding so easy and natural.)