The future of programming – a brief look at two functional programming languages
I have been a software developer for more than 8 years now, and all this time I have been developing software in imperative languages like C++, Java and C#. During the years I have occasionally had a taste of functional programming in the language Haskell. The interest has never been big enough to do some real software development, but lately I have looked more into a couple of other functional programming languages, namely Erlang and F#.
What is functional programming
Functional programming is a different programming paradigm than what most programmers are used to. Programs written in imperative languages like C++, C# and Java are sequential programs, whereas programs written in a functional programming languages (FPL) is an expression, an is evaluated as such.
So in a FPL you write expressions, and in imperative languages you write methods. Code written in a FPL is more concise and easier to understand, and in short the benefits of using a FPL are listed here:
- More concise code, which means faster development time and fewer bugs.
- More comprehensible code, which again leads to fewer bugs and higher reliability.
- Immutable state, which leads to more reliable programs since writing multithreaded programs is easier without having to worry about shared state, locks and the problems that arise from this.
But don”t take my word for it. Try it yourself – I have listed a few links at the bottom of this post to some FPLs and articles. Also, you should read John Hughes article from The Computer Journal here: Why Functional Programming Matters.
An example
As an example I have written two small programs which calculates the number of primes between 2 and 9,999,999. The first one is written in C#:
static void Main(string[] args) { int count = 0; for (int candidate = 2; candidate <= 9999999; candidate++) if (IsPrime(candidate)) count++; Console.WriteLine(count); } private static bool IsPrime(int candidate) { int max = ((int)Math.Sqrt(candidate)) + 1; for (int divisor = 2; divisor <= max; divisor++) if ((candidate % divisor) == 0) return false; return true; } |
and the second in F#:
#light module PrimeCalculator open System let isPrime candidate = match candidate % 2=0 with | true -> 0 | _ -> let rec isDividable candidate divisor max = match candidate with | _ when divisor>max -> 1 //it is a prime | _ when candidate % divisor=0 -> 0 //not a prime | _ -> isDividable candidate (divisor+1) max isDividable candidate 2 ((int)(Math.Sqrt((float)candidate))) let rec countPrimes n max count = match n>max with | true -> count | _ -> countPrimes (n+1) max (count+(isPrime n)) let primeCount = countPrimes 2 9999999 0 printfn "#primes = %d" primeCount |
A much more succinct and readable version can be seen here. Notice however that this version does not perform as fast as the version above.
#light let rec isPrime n divisor = match n with | _ when divisor>(int)(sqrt ((float)n)) -> true | _ -> if n%divisor=0 then false else isPrime n (divisor+1) [2..9999999] |> List.filter (fun x -> isPrime x 2) |> List.length |> printfn "#primes = %d" |
Since this is F#, and F# is a superset of OCaml, you can use object oriented programming mixed with your functional programming. But you can also use imperative programming mixed with either functional or object oriented code. The following code does the same as above, but uses imperative code to iterate through all possible prime candidates. This version is even slower, but much less RAM intensive.
I will get into the performance of the different coding styles in a later article, since this is obviously a major factor. This isn’t a surprise since F# is a functional programming language. More on that later.
#light let rec isPrime n divisor = match n with | _ when divisor>(int)(sqrt ((float)n)) -> true | _ -> if n%divisor=0 then false else isPrime n (divisor+1) let mutable count = 0 for x in 2..9999999 do if isPrime x 2 then count <- count+1 printfn "#primes = %d" count |
Another example of how concise and expressive a FPL is, is this implementation of the quicksort algorithm written in Haskell:
qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) |
Erlang
One of the languages that I think will have a profound effect on the future of programming is Erlang. The reason I think that is, that it is the language for writing parallel, concurrent and distributed applications at the moment; it was designed with parallel and distributed computing in mind, as I wrote about in an earlier post.
In Erlang each thread (they call it a “process”) is extremely lightweight compared to a normal operating system thread: an Erlang process takes up 300 bytes of space (plus the state, of course), with the result that the scheduler can switch between them in no time. A typical Erlang system runs several thousand processes without any discernible performance penalty.
Another thing that distinguishes Erlang from most other functional programming languages (FPL) is how easy it is to learn. In contrast with some other FPL”s (and I have seen some really esoteric ones) it is simple and intuitive; It took me a few days of reading and writing to learn the basics of the language, how to spawn processes, and how to write distributed applications.
The only drawback of Erlang is its performance, which is quite poor compared to C# or other modern imperative languages. But it is only few types of applications that really need to keep the CPU(s) busy all the time – a lot of the time the CPUs are idle; a normal client- or server application that is constantly in the red is usually sick in one way or the other.
F#
F# (pronounced F-Sharp) is a relatively new programming language from Microsofts research department. It is a dialect of Objective Caml (OCaml), and you can in fact compile most OCaml code with the F# compiler and run it without any modifications. F# gives you all the benefits of a FPL, and a performance profile comparable to that of C#. I don”t mean to sound like a bad commercial, but the benefits are all too clear to me – in some applications performance is paramount.
Besides, in november 2007 Microsoft announced that it will move the language from research to the list of supported .NET programming languages.
What makes this language so important is that it is compiled and run on the normal .NET runtime, just like any other .NET language. First of all this gives us the benefit of using a virtual machine that has been tested and optimized through several years. Second of all – in line with the .NET “mantra” – F# gives programmers the ability to interface with other .NET languages and the huge framework behind the .NET runtime. And I don”t mean “interfacing” as in using some kind of language bridge. No, it is native: from F# programs you have access to the framework available to all other .NET languages, and from any other .NET language you can use components written in F#.
This gives programmers a big advantage when writing software. You can for example choose to write the user interface code in an imperative language like C#, and write the data processing code in F#, which is much more concise and intuitive when it comes to expressing algorithms and formulaes in code.
The ability to seamlessly combine two or more languages is the true strength of F#. I intend to explore that fact more when researching genetic programming, by utilizing my MPAPI framework to build parallel and distributed applications, with the core functionality developed in F#.
What do you think? Is this where we are heading in the future, is this where we should be heading, or is the various projects to parallelize (and further complicate) the languages as we know them the way to go? Try googling “parallel computing” on Google’s blog search and see what”s cooking in this interesting area.
I beleive the reasons why Erlang uses the name “processes” rather than “threads” is because threads use shared memory and Erlangs processes dont.
Comment by Jon Gretar — June 25, 2008 @ 16:31
Hm, that makes sense. In my (small, granted) world I just think of a process as something more heavy weight than a thread. But working with Erlang has been an eye opener in a lot of ways
Thanks for the input
Comment by frank — June 25, 2008 @ 20:53
I really believe that the key thing to making concurrent and distributed programming work is not only isolation of concepts, but isolation of execution.
Code level integration such a F# provides makes it easy to write code that propagates mutable state between conceptually isolated components. Granted, a good code review should pick this up and squash it, but its still a major opening for deeply complicated bugs to crawl through.
I really like Erlang’s ports approach to external integration. Effectively the code from the other language can be treated like another process/actor in the system. However, a lot of work could be done to smooth over the sharp edges of this mechanism. Erlang needs a dedicated effort to bring it up to par with the exceptional polyglot programming capabilities of the CLR.
Comment by Alain O'Dea — June 26, 2008 @ 2:56
We have already shipped several commercial products written entirely in F# and are finding it to be an incredibly productive language with great prospects in the real world.
Always great to see other people interested in F#!
Comment by Jon Harrop — June 26, 2008 @ 20:25
To Alain O’Dea:
I haven’t had enough time to look at how Erlang supports external integration, but it looks like at good approach. Nonetheless it is still a sort of “hack”, and – as you write – it needs a lot of work. Hopefully that will happen in the near future as this language grows a large user base.
I agree with you that the possibility to propagate mutable state in F# is a serious problem when writing good, stable software, but that is exactly Erlangs strength, not F#’s. One of the strengths of F# is that it is so easily integrated with other languages, which – in my oppinion – makes it the number one choice at the moment; The ease by which I can write code in both F# and C#, and use them interchangeably, is very powerful. The GUI-parts of an application, for example, is still easiest to write/design in one of the supported imperative languages like C# or VB.
But let us see what the future brings – a lot of interesting things are happening at the moment.
I like both languages because they each have properties that makes it easier to write large scale applications, but perhaps we will see a hybrid language soon..?
One of the things that I miss in F# is the possibility to spawn miniature threads (I believe the correct term is “fibers”) much like Erlangs processes.
Comment by frank — July 16, 2008 @ 8:53
To Jon Harrop:
Thanks for your post. It is great to know that there are developers out there doing serious work with this language and making a buck on that.
Historically the use of functional programming languages has been the domain of universities and researchers, but lately I have come to know several companies that use either F# or Erlang in serious work.
I will do my part to do the same in my company
Comment by frank — July 16, 2008 @ 8:56