It's hard to define exactly what a "functional language" is--out of the languages you listed, only Haskell is purely functional (all the others adopt some sort of hybrid approach). There are certain language features that are very helpful for functional programming, though, and Ruby and Python don't have enough of them to be very good environments for FP. Here is my personal checklist, in order of importance:
- First-class functions and closures (Ruby, Python, and all the others you listed have this).
- Guaranteed tail-call optimization (Erlang, Haskell, Scala, and Scheme have this, but not Python, Ruby, or Clojure (yet)).
- Support for immutability in the language and standard libraries (this is a big one that all of the "functional languages" you listed have (except Scheme) but Ruby and Python don't).
- Language-level support for referentially transparent (or pure) functions (as far as I know, only Haskell has this currently).
The need for (1) should be obvious--higher-order functions are extremely difficult without first-class functions. When people talk about Ruby and Python being good languages for FP, they are usually talking about this. However, this particular feature is necessary but not sufficient to make a language good for FP.
(2) has been a traditional necessity for FP ever since Scheme was invented. Without TCO, it is impossible to program with deep recursion, which is one of the cornerstones of FP, because you get stack overflows. The only "functional" (by popular definition) language that doesn't have this is Clojure (because of limitations of the JVM), but Clojure has a variety of hacks to simulate TCO. (FYI, Ruby TCO is implementation-specific, but Python specifically does not support it.) The reason TCO has to be guaranteed is that if it is implementation-specific, deep recursive functions will break with some implementations, so you can't really use them at all.
(3) is another big thing that modern functional languages (especially Haskell, Erlang, Clojure, and Scala) have that Ruby and Python don't. Without going into too much detail, guaranteed immutability eliminates whole classes of bugs, especially in concurrent situations, and allows for neat things like persistent data structures. It's very difficult to take advantage of these benefits without language-level support.
(4) is, for me, the most interesting thing about purely-functional languages (as opposed to hybrid languages). Consider the following extremely simple Ruby function:
def add(a, b)
a + b
end
This looks like a pure function, but because of operator overloading, it could mutate either parameter or cause side effects such as printing to the console. It's unlikely that someone would overload the +
operator to have a side effect, but the language gives no guarantees. (The same applies to Python, although maybe not with this specific example.)
In a purely functional language, on the other hand, there are language-level guarantees that functions are referentially transparent. This has numerous advantages: pure functions can be easily memoized; they can be easily tested without relying on any sort of global state; and values within the function can be evaluated lazily or in parallel without worrying about concurrency issues. Haskell takes full advantage of this, but I don't know enough about other functional languages to know if they do.
All that being said, it's possible to use FP techniques in almost any language (even Java). For instance, Google's MapReduce is inspired by functional ideas, but as far as I know they don't use any "functional" languages for their large projects (I think they mostly use C++, Java, and Python).