Ask HN: Is functional programming really worth it?
There's Arc. There's OCaml, Haskell and a plethora of so-called next generation languages. These languages are supposed to be far superior to today's measly languages like Java or C++, and in pg's own words: they're supposed to make you more productive, get work done quicker. (Quote from Revenge of the Nerds: "1 line of LISP replaces 20 lines of C")
But is that really true? From experience I can tell you the lack of libraries in these languages kills off almost any code size advantage you get from using them. So I ask you all, be honest to yourself; is using such a language worth it?
And one last thing. Is functional programming really better than imperative programming? All that brain-racking to implement a multi-level loop using recursion in ML? IMO, imperative programming cleanly maps out to the real world. The world is imperative! Things have state! We do not live in an imaginary fluffy world of mathematical elegance & correctness. Truth is it's so much more easier to think imperative.
So, Is functional programming really worth it?
84 comments
[ 3.6 ms ] story [ 163 ms ] threadAnd one last thing. Is functional programming really better than imperative programming? All that brain-racking to implement a multi-level loop using recursion in ML? IMO, imperative programming cleanly maps out to the real world. The world is imperative!
"LISP embodied a much greater leap of imagination. Conceptually FORTRAN remained on familiar grounds in the sense that its purpose was to aid the mechanization of computational processes we used to do with pen and paper (and mechanical desk calculators if you could afford them). This was in strong contrast to LISP whose purpose was to enable the execution of processes that no one would dream of performing with pen and paper." - EWD 1284
I would argue that if one needs to know where to look and how to manage them, then CL _is_ lacking in terms of libraries. One first has to know where to look and find a CL implementation then to know where to look and find batteries for it.
You don't complain about not knowing where to look to get a Linux distribution do you? Freedom leads to fragmentation, but the fact that there are a million Linux distributions doesn't make Linux any less powerful.
If you want an Ubuntu, don't use Lisp. More power to you. Some people just want to get the job done quickly with at little thinking as possible. I know I feel that way sometimes. But if you want an Arch, where you built the system from the ground up, then you want Lisp. And at the end of the day when something breaks, you just might know how to fix it. ;)
It's worth it, but it's not a magic bullet. Language wars are for people who like tools better than they like workshops.
This becomes a "war" because the other side can't believe that there is something that they don't already know, and they will defend that illusion to the death. It's quite hilarious... in a sad, sad way...
As long as there are sides there are wars. The thing to recognize is that there are no sides, that we're all in the same boat and that we all have something to contribute. Convincing someone of the validity of an argument - especially against the 'unbelievers' - takes time, patience and diplomacy, not a war.
I agree that trying out a new language or technique can make life measurably better, but if you were a FP 'die hard' that has never written a line of imperative code in their whole lives you'd probably stand just as much to gain. This whole FP is 'better' argument is getting a little tired, better means 'always better' and that is not something that I consider proven - for now.
But keep in mind, you are hearing this from people that don't know anything about FP. The person who posted the Ask HN admits as much -- "I hear FP is awesome, but I don't want to learn it. Will someone please rehash at least eighty-nine reasons why I'm missing out? Because if there are only eighty-eight reasons, I am going to smugly declare that FP is worthless."
Let's face it, there is a such thing as a dumb question, and this is one.
I'm reserving my own judgment until I've gotten good enough at it that I can rightly say I can make up my mind by myself without external input.
That point is probably a long way off, but any attempt at ramming something down my throat (be it IP or FP is besides the point) is met with resistance.
Arguments, not rhetoric persuade people.
If FP were obviously better and as easy to learn as some claim then these arguments would be superfluous, the fact that it isn't means that the case is not as clear cut as some make it out to be.
Let's take HN as an example.
It's a site built by a man that is considered to be God himself when it comes to functional programming, working in a language that he himself states is going to be a 100 year language.
The site suffers regular outages, has serious limitations with respect to functionality that is present but simply does not work and small changes are apparently so hard to add that it takes months for even the simplest items. The way to deal with load issues is by cutting functionality (see the length of the per user comments list, new pages and so on).
Mr. Blub himself would have fixed these things long ago and would not tolerate such a state of affairs, programming in visual basic .net if that's what it came to.
If that's the 'poster website' for FP then I think that I'm either missing something or FP isn't as good as it is cracked up to be for the environment that most of the people here are working in (web programming).
Possibly a port of HN to a more stable platform (say clojure) would remedy some of these issues, but if PG can't 'hack' it then who am I to even try.
news.arc has its limitations, but it's not because of functional programming; it's because pg tried to do as many things "differently" as possible (in-memory data-store, no "objects", etc.). The goal of news.arc, I assume, was to play with ideas, not to build a website. The website is just a side effect.
I won't debate the merits of news.arc's design, but I will note that not everything is about "building a scalable website". There is more to life than the output of ab.
my own experience is that learning functional programming gave me more options - that can be useful when solving problems. when writing code for myself i try to combine the best of function, oo and procedural styles, while staying within "good taste" for the language i am using (sometimes i cannot choose the language).
so yes it's worth it, but your question leans too much to extremes: it's not a silver bullet any more than oo was; it's a mental tool that does help in every-day programming.
Functional programming is not always the optimal choice. But when it is, it is awesome.
Regarding "betterness", it's a philosophical shift -- some problems are better suited to one domain or another. My favorite metaphor for functional programming is Unix pipes:
cat file | grep XYZ | sort -r -n | ...
There's no explicit state but you can track the flow... output a file, search for something, sort that result, pipe it somewhere else, etc.
Imperative/state-based programming forces you to give names to all those intermediate results, just so you can pass them along. Sometimes variables are just a place to hold data until you can pass them to the next function; functional programming makes that so easy you don't need the variable. And think about how much easier it is to debug; once you get a part of the pipeline working, you can move to the next part (without worrying about global variables and other hidden interactions that can happen with imperative programming).
Also, your comment of:
The world is imperative! Things have state! We do not live in an imaginary fluffy world of mathematical elegance & correctness.
I would check out article by Rick Hickey on state on the Clojure website:
http://clojure.org/state
It will get you thinking about how often your objects really need to have "state".
cat(file).grep('XYZ').sort... ?
Imperative / functional languages are equally powerful, but are they equally expressive? There's a reason I wrote the above in a shell script vs. an OO-language (or C): shell scripting captures my intent with minimal overhead, with the right primitives built-in.
OO-languages force you to define classes, methods, type input parameters, etc. up front. So if you know you just want that exact workflow then you can model it.
What if I want to customize the sort routine? Well, I guess I can define a sorting interface and take that as an argument.
And what if I want a more flexible data structure? (With unix it's all text, but why not a hashmap being passed around?). Well, I can define a generic "list" class with an infinite number of members, and all the operations change this list.
But now we've gotten away from OO programming (saying data is of type "List" is not saying much at all).
We're basically trying to recreate functional programming ideas:
* Simple, flexible, built-in data structures (list of lists)
* Pass around functions at runtime, as you please (no interface to define, instantiate an object of that interface, and pass in)
* Composable functions (at runtime, decide that function Z is really F(G(x)))
It's not that the operations aren't possible, but OO languages seem to lead you down a specification-first path, usually customized to a problem domain.
You can "get around" this by using design patterns like Strategy, but again, you're recreating functional ideas in another language. Not that it's impossible, but it's not quite suited for the problem domain (like doing text processing in C vs. using perl... yes, you can find a regex lib, and a better string class, but why not use a language with it built in?).
Hope this helps -- different languages help you think differently. I've used C# a lot and really love the mix of functional and imperative styles in the latest versions (3.5). Joel had a good post (http://www.joelonsoftware.com/items/2006/08/01.html) on this regarding map/reduce: it's not that it's "impossible" in OO languages, but you don't instinctively think about ideas like "passing around a function and apply it to data".
Seriously though, if you want to try both perspectives, you have to submerge yourself in a functional viewpoint until you start thinking like that. Because if you grew up thinking functionally, you'd find it easier than imperatively.
When you get down to the silicon there's only data and instructions to manipulate that data. Everything else is abstractions poured over the top to suit specific application domains.
From PG's famous essay (even quoted on WikiPedia):
The other point is that there are pure OO languages, pure functional languages, pure logic languages, pure imperative languages, and rarely is a problem easily and comfortably expressed entirely in one pure paradigm.But learning, properly learning a functional language gives you more experience, more ways of thinking, more techniques, more options.
But remember:
I've seen ForTran programs written in Haskell, believe it or not. You need to learn not just the language syntax, but the underlying ideas and techniques.Then you can make the choice.
I once wrote a toy library that lets you write BASIC in Haskell:
(Yes, line numbers and all.)Lennart Augustsson extended that to allow things like "10 PRINT 2 + 2" (no parentheses needed), along with most of the rest of BASIC; his approach effectively implemented a full parser and treated everything as a token. He also added LLVM-based code generation. See http://hackage.haskell.org/package/BASIC .
I understand functional programming perfectly well. It's just that for most of the time, it's easier to think imperatively. Yes, there are some special problems (Towers of Hanoi, Ackermann's) where functional programming is truly the way to go. But in most cases it isn't.
FYI I use gotos in my C programs when I see it's the easiest & clearest to get something done. I think you should go with what best suits the situation.
What troubles me though is when you go out of your way to do everything functional-style, even when you know there are easier ways of doing it. And the problem is this happens way too often to be ignored. Some functional programmers just push the functional idiom into everything they do, to such a point that understanding their code is like solving a sudoku puzzle.
Functional programming imposes some constraints that range from mild discipline to mind-bendingly difficult. However in general I can't think of many constraints that provide a better power-to-weight ratio (garbage collection is probably one).
If Haskell was the dominant language and functional programming was how everything was done all the time, then I could buy into the argument that there are easier ways to do things. However as it stands, not enough people really grok functional programming (myself included) to optimally apply those principles towards the creation of robust systems in non-pure languages.
That's perfectly okay, of course. It just means it's a little early to start making final conclusions.
> What troubles me though is when you go out of your way to do everything functional-style, even when you know there are easier ways of doing it.
The point is that you (to possibly generalize a bit since I don't know you) are 'going out of your way' to write everything in an imperative style because you don't know there are easier ways of doing it.
This is exactly what the blub story is saying. You are looking 'up' at functional languages and saying "in most cases that's just esoteric weirdness that I don't need" because you think in blub.
Here's an alternate version of your last sentence from a hypothetical functional programmers perspective:
> Some imperative programmers just push the idiom into everything they do, to such a point that understanding their code is like untangling spaghetti.
Maybe that's because I'm a 'Blub' programmer, in spite of some real effort to 'dig' the functional programming method, maybe it is because I think that it exposes a sort of smug attitude towards those that program in 'lesser' languages.
There is a taste of an attitude there that I can't rhyme with the other things that PG wrote.
You don't show the advantages of a language or a technique by making the argument about people, especially not by talking down to them from that high up.
As I continue to learn more about them, so I continue to become a better programmer even in languages that don't exactly match the paradigms. Learning structured programming has made my assembly coding better, even when I use all the nasty tricks available. Learning about FP, logic and constraint programming has made my C++ and Python coding better.
I'm sure I don't yet know everything, or even half of everything. I don't (often) use a pure FPL, but knowing, really knowing, about pure FP has made my thinking better.
Who knows what remains to be discovered or invented that will expand our thinking further.
I'm on to that, as you know from our email exchanges, but I don't like the idea of presenting the one as 'better' than the other.
Both FP and IP have earned their place in the toolbox, and even though I'm much more familiar and comfortable with the 'IP' portion of it I can see the power of some of the concepts behind FP. But I find it very hard to believe that FP is so much better than IP that it warrants talking down to the people using it.
> As I continue to learn more about them, so I continue to become a better programmer even in languages that don't exactly match the paradigms.
Agreed, and even while I definitely have not 'grokked' functional programming yet to the point where it would make sense for me to write some small utility in an FP language I can see how just learning about FP has influenced my coding style in other languages.
> Who knows what remains to be discovered or invented that will expand our thinking further.
Working on that :)
It's mostly a matter of constraints on this end, I probably took on more than I can handle in terms of work that simply needs doing and I hoped that through FP I would find some shortcut allowing me to become more productive. It has worked in a sense, some of the code I wrote for other projects has become more clean because of reading about and playing with FP, but in a concrete sense where I can substitute a functional language for an imperative one and get more mileage out of my days it hasn't (yet).
But I'm optimistic that one day it will, and as long as I can see it influence my thinking in a positive way I'll continue to study it.
One of the biggest eye openers so far was 'memoization', I'm not even sure that I understand it well enough to paraphrase it, but when looking at the source of hashlife it suddenly clicked: When you work in a functional way and you are guaranteed that there are no side effects stemming from lower layers you can replace the call to a function with a bunch of arguments, no matter how complex with its result. That literally blew my mind, even though in hindsight it is a blindingly obvious thing.
That such a 'small' thing as side effects could have such polluting effects on higher level code that a thing like memoization is impossible gave me a lot of energy to continue my 'quest' :)
Must have been messy.
I don't think that this means an imperative language is therefore better than a functional programming language. Many problems would be better solved with more 'mathy' models.
I'm not accusing you of this, but it does drive me a bit bonkers when code becomes anthropomorphized, e.g. "this guy talks to that observer, who sends a message to the account manager." This type of code is frequently not very modular and hard to maintain. A functional programming language makes it hard to think like this, and IMHO that's a good thing.
Processes map well to functional programming, but then when we look at what is being processed, it often has to have state and have specific behaviors. So choosing the best fit abstraction is critical to clean implementations.
Better libraries can't give you the compile-time error checking that a good compiler can. In Haskell, I half-seriously joke that "it compiles, it must be correct".
Also, modern functional languages have great library support. For instance, Haskell has a variety of libraries available, and a great FFI to C that lets you talk to any available C library. (I consider an FFI a necessary part of any generally useful language.)
And finally, "implement a multi-level loop using recursion in ML" sounds like a learning exercise, not a useful programming technique. If your code tries to recreate constructs like multi-level loops, you've done something wrong; most of your code shouldn't even need explicit recursion. Don't try to write one language's code in another language, and especially don't try to emulate an imperative language in a functional language. Learn how to do things the right way in the new language. If you try to write Java in Haskell, you'll find that it feels a lot harder than writing Java in Java, shockingly. :)
You've learned to think imperatively. Functional programming requires a different approach. It took me a long time to learn to think in Haskell, but now I feel quite comfortable with it. Having watched and helped non-programmers learn to work with imperative programming, I know that doing so requires some hard leaps as well. ("Wait, x = 5? But x = 4 earlier, so how can x = 5 now?")
Many in the functional world realize that some things are akward (see Peyton-Jones's talk "Wearing the hair shirt" about Haskell).
To answer the question about loops with recursion: you seldom write recursive functions when programming functionally but instead cast things into maps, filters and reduces. Thus it's not such a big problem (and the cool thing about using map & family is that you can replace them with parallel versions quite easily). With that said, some practical functional languages provide sugar to write seemingly imperative loops.
In terms of libraries, at least Scala, Clojure, F#, CAL, Nemerle (amongst others) ingrate well with either the JVM and/or the .Net platform. You can implement Erlang nodes in any JVM language (which I grant is not as good as calling directly into the JVM from an Erlang function).
There are definitely advantages to writing as much of your code as possible to avoid side effects (obviously ignoring the odd print statement to help with debugging), since your code becomes easy to test and easy to reason about. But in real world apps you will need state management - Clojure and Scala both allow this (in different ways) and I'm sure that F# does too (though I've never worked with F#).
I think that these three languages address many (all?) of your concerns. They all have cool user communities, so give one of them a shot.
Don't forget unfold! http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.42.1...
Highly recommend to Perl & non-Perl programmers.
However, if we ask ourselves whether functional programming is worth learning (as I suspect the poster is really asking), then the answer is clearly yes, if for no other reason than that's the only way you can make an informed decision as to whether it's worth applying FP to that particular problem of yours.
There is really only one difference between FP and traditional imperative programming (which I'll call IP). In IP you have direct control over where the results of computations are stored and in FP you don't. This is, of course, a two-edged sword. Control gives you both power and the ability to screw things up. Lack of control makes it easier for your compiler to perform certain kinds of optimizations.
The reason learning FP is worthwhile is not so much because FP is some kind of magic bullet, but because understanding the tradeoffs between having this kind of control and not having it is worthwhile in and of itself. If you understand those tradeoffs you will write better code in whatever language you use, even if it's assembler.
- lisps: scheme, CL esp. SBCL, clojure
- F#, scala
- erlang, ocaml, haskell
here's some books, freely available content, including for ocaml and haskell
http://www.freetechbooks.com/functional-programming-f34.html
but missing scala: http://programmingscala.com/
here's a STM and/or actor/ message-passing vs. lock/monitor/mutex /semaphore reading list:
http://www.tbray.org/ongoing/What/Technology/Concurrency/
http://www.sauria.com/blog/2009/10/05/the-cambrian-period-of...
http://www.scribd.com/doc/28336206/Multicore-programming-in-...
http://groups.google.com/group/clojure/browse_thread/thread/...
http://blogs.azulsystems.com/cliff/2008/09/jvm-language-su.h...
http://wiki.jvmlangsummit.com/pdf/36_Click_fastbcs.pdf
- haskell, F#, scala are state of the art as far as type inference:
http://blogs.tedneward.com/2010/02/14/Dont+Fear+The+DynamicV...
- scala has become a actor/message passing and STM lab:
http://osl.cs.uiuc.edu/docs/pppj09/paper.pdf
http://www.stanford.edu/class/cs94si/Concurrency.pdf
http://github.com/djspiewak/scala-stm
http://akkasource.org/
Yes, all true. Also, the world is extremely concurrent, made up of billions of independent actors sending each other messages.
That's why we need to use Erlang, an imperative, stateful, mathematically inelegant language.
Might be true. But in my experience that single line takes as much time to think up as the 20 lines in C. So it doesn't really work as an argument for either.
Some basic things which i didn't knew in the begining :
Fast persistent data structures - Coming from imperative langs, one might be hesitant to write functions which take a million elt vector, modify it and return a new vector. It is important to know that any good implementation of functional data structures will allow you to do this in constant time & speed. (independent of the size of the data structure).
Editor combinators : If you arent aware of this & are modifying a data structure with several levels of nesting, imperative assignment will seem much more convenient, ie dont translate foo.bar[8]=3, into something of this pattern barNew = editVector(foo.bar, 8, 3), fooNew=editMap(foo, "bar", barNew). It gets worse with more levels. Instead, use an editor-combinator library which will allow to you specify the path within the data-structure and a new value just as in the imperative case.
Things to know which are not strictly FP related, but nevertheless very useful - pattern matching simplifies definitions a lot, typeclasses are usually more convenient than class hierarchies.
Someone has already pointed out advice on libraries, (Clojure or any using any popular language in a functional way).
The main thing which motivates all of this - composability. It is hard to combine procedures with side effects into compound procedures because one needs to keep track of whether the component procedures are interfering with each other. FP eliminates this friction in composition.
As someone who has programmed extensively in Arc... you're right. The language is very pretty and very concise, but to get things done, I find myself going back to Python (and to get things done quickly in the execution sense, C++). If only Arc weren't so slow and had more libraries...
Haskell: where the great ideas are coming from, although early implementation decisions have made those a bit painful to use (language extensions). It's probably the most "mind-bending" language out there so I recommend it for growth.
Ocaml: excellent and very simple language, with a kick-ass garbage collector. The worst thing about it is the associated build system, OMake, which is almost as ugly as Make. Most people who are turned off by Ocaml are actually turned off by the build environment or by the Ocaml team's resistance to improvement. (The standard libraries are really terrible; if I recall correctly, List.map isn't even tail-recursive).
Clojure: Great compromise between power of language and library support, because it runs on the Java Virtual Machine. It's a Lisp, so it's dynamically typed. It also has some warts related to its Java roots; for example, 43 and (biginteger 43) count as separate keys in a map. However, these are easy to work around.
* 1918 libraries for Haskell (according to http://hackage.haskell.org)
for comparison, there are 1420 Erlang libraries (according to http://projects.trapexit.org/web/)
Was there something you were looking for that's not on Hackage?
I don't want to weigh in on the general 'are functional languages' better debate (as I don't have enough experience of them), just wanted to share that as a data point.
Save lines of code != get work done quicker. What might appear as a paradox should be obvious: we spend more time debugging, modifying, expanding and organizing than writing.
IMO, imperative programming cleanly maps out to the real world.
More so does CUDA with a random number generator. You can't get more precise than quantum (yet).
Truth is it's so much more easier to think imperative.
That's like saying it's easier for a Chinese to speak Mandarin than for a French.
So, Is functional programming really worth it?
To use, it depends. To learn, yes. One can't learn too much in life.
Could someone that has used both statically and dynamically typed functional languages comment on what they liked and disliked from both experiences? I am interested in exploring a functional language because they seem to have cool ideas in them, but I'm not sure what road to start down.