In the near past I had a "little" discussion with a group of functional programming fundamentalists. The discussion, considering mainly F#, lasted for months and in fact it's still alive to some extent. Those posts actually strenghtened my opion about functional programming fundamentalists: most of them simply don't know well enough "the base" so that they could do any working solutions with OO-languages (or any?) utilizing multiple cores. For example, volatile variables were considered mostly mysterious. They presented several code snippets which were based on wrong assumptions about what concurrency and parallelism mean and when parallelism can actually happen. Quite often the answer is: "it just works". But does it work most of the time because of good (or bad) luck?
While we were filling the thread with posts, I found some really "nice" examples by Googling F# activities. The search resulted in several "diamonds", this being one of my favorite (from Misusing mutable state with F# Asynchronous Workflows):
"So it could be that the increment operation gets executed on two or more threads at exactly the same time. And if two threads read the variable at the same time, and then increment it before saving it back, we effectively lose one of our increment operations.
This is all fairly unlikely ..."
How about having that fella programming your banking solution? Not! If you know how locks, volatile variables and atomic types work, you can see in a second what's wrong with his program.
Fundamentalists swear in the name of immutability; all operations are acting on immutable data structures. In practice, applications need to have some side effects like it or not. Simon Peyton-Jones, a major contributor to the functional programming language Haskell, said the following: "In the end, any program must manipulate state. A program that has no side effects whatsoever is a kind of black box. All you can tell is that the box gets hotter." Admitting that you need side effects means is a good start. I think the key thing to understand is visibility: when a thread modifies a memory location, how, and when, is that change seen by other threads? (If you need more details about and cannot get sleep easily at nights, search for "Memory Barriers: a Hardware View for Software Hackers"). Sooner or later you will find yourself synchronizing access to shared mutable state and you'd better understand how it works. Let's consider a sample: a web crawler in F# by Tomas Petricek (http://fssnip.net/65). When it's time to check whether given URL is already visited, he falls back to object oriented, concurrent data structure called ConcurrentQueue. But he knew the data structure must be synchronizing one.
Functional programming languages very often use compact syntax which actually makes it really hard to understand. Fundamentalists think it's cool, but to me those programs often look like fonetic symbols of puking.
All this ranting doesn't mean that I don't understand the value of functional programming principles. Yes I do, and actually use them in many languages, but I don't believe in pure functional languages or even hybrids without understanding the base. Anyhow, be aware that functional features are being stolen from old languages like LISP and are now or in the near future in modern OO languages (like Java and C#). I would say that in the end of day, these are the big winners. For example, Scala will probably loose many followers when Java 8 is released with it's (not so sexy?) lambda expressions and built-in map/reduce (via java.lang.Iterable), etc. For those of you who don't know what the lambdas in Java will look like, here's a short sample:
import java.util.*;
public class Test {
public static void main( String... args ) {
Collection<Integer> list = Arrays.asList( 1,2,3,4,5 );
System.out.println( list.filter( x -> x > 2 ) );
}
}
Using OO- and functional techniques together is definitely OK and I believe it is an approach what will eventually win. Now you can still work with your favorite mainstream language (one of top five in Tiobe listing) and have excellent libraries for concurrent programming. As a bonus, you don't have to struggle with version imcompatibilities (like in Scala). Last but not least is the fact, that functional programming paradigm has no proper modelling supports, such as UML.
So, do yourself a favor by stopping playing with functional programming languages and starting to learn your mainstream language properly (including how to do concurrent programming with it).
No comments:
Post a Comment