top | item 6960239

Why functional programming?

48 points| AnimalMuppet | 12 years ago | reply

I've been seeing all this stuff about functional programming, and I don't get it. Why should I care?

I'm not trying to be a troll. I'm trying to understand. And I don't.

I've been a professional programmer for 28 years. I don't care about "the one right way" or "ideological purity" or anything like that. I care about efficiently building programs that work. (Answers that say that functional programming is in fact the one right way will be dismissed as ideologues or trolls.)

I don't think that functional programming can be the one right way, because I have spent most of my career in embedded systems. Those programs tend to be full of states that changed many function calls ago, but are still relevant. If I understand correctly, that doesn't play well with functional programming.

But once you don't buy that functional programming is the one right way, then you have to ask, "When is it the right tool for the job? And when is it not?"

(And by the way, once you don't' think that functional programming is the one right way, then things like tail recursion and the Y combinator stop looking like elegant solutions to important problems. They start looking like crocks that you were forced into because you're using the wrong tool for the job.)

Look, I get that all of computer programming is mathematics. It's all Turing machines, too, but that doesn't mean that I want to write programs like I'm writing on a Turing machine. I don't want to write them like I'm writing an abstract algebra paper, either - unless there's a real benefit.

Again, I recognize that this sounds like a troll, but I'm really not. I can be convinced. Somebody say something convincing (hopefully in terms that a professional programmer with very little abstract algebra can understand).

57 comments

order
[+] jerf|12 years ago|reply
You appear to know what you can know from the outside. The only thing left is to try it out. Or choose not to; if you're that deep in embedded programming its utility to you will probably be marginal.

One bit of advice though; "trying it out" means getting to the point where you can actually "do things" with it, not running through two tutorials, filtering a couple of lists and writing a factorial function. That's the equivalent of learning how to write an if statement or a for loop or something, not the payoff. It's likely been a long time since you've actually felt like you don't know what you're doing, but that will be a sign you're on the right track.

You might also be interested in this: http://www.jerf.org/iri/post/2908 Being in the embedded world, you've probably just gotten used to certain things being "just the way it is" after 28 years, but one of the things that FP can show you is that those things aren't "just the way it is"; they are that way as a result of the choices we've collectively made. You've probably independently partially rediscovered a lot of FP's insights about state management over the years; you've probably also not independently pushed it as far as the FP community as a whole has. We don't actually have to live in a world where a flag flipped 25 million instructions ago is now causing a crash in seemingly unrelated code; we chose that world. It is not without its advantages, but it is not all gravy either.

[+] AnimalMuppet|12 years ago|reply
That was really interesting. Thanks.

And even in the embedded world, "constrain the state space like your life depends on it" is still very sound advice, even if you can't go fully functional.

[+] seiji|12 years ago|reply
Revisit the Blub Paradox: http://www.paulgraham.com/avg.html

In short: Languages less powerful than Blub are obviously less powerful, because they're missing some feature he's used to. But when our hypothetical Blub programmer looks in the other direction, up the power continuum, he doesn't realize he's looking up. What he sees are merely weird languages. He probably considers them about equivalent in power to Blub, but with all this other hairy stuff thrown in as well. Blub is good enough for him, because he thinks in Blub.

Interestingly enough, I think the Blub Paradox greatly informs how pg views people too. You can be a "normal person" or you can be a "super smart person." Normal people can't understand super smart people because it's just beyond them. (part of the whole "people aren't created equal—some are just better than everybody else" selectivist philosophy.)

[+] ColinWright|12 years ago|reply
tl;dr - you don't need to care, you can do perfectly well without it, but you're missing something.

Longer answer ...

There are several issues here, and many people conflate them. They are realted, and I'm not going to be able to do them justice in a quick reply here. It's Christmas Eve, and I have a lot to do.

So, briefly ...

Learning FP, really learning it, gives you a new insight into programming. It can change the way you work, and the way you think. If nothing else, it's mind expanding, and that's always a good thing.

There's a seminal paper "Why Functional Programming Matters"[0] (WFPM). It dates back to 1984, but it's still relevant. In fact it's so relevant that raganwald[1] wrote "Why Why Functional Programming Matters Matters." That's also a really good read, with many useful insights.

fogus[2] has included WFPM in his list of 10 technical papers every programmer should read twice.[3] There's also a StackOverflow discussion[4], probably several.

There are many, many programmers out there, capable, productive, useful programmers getting stuff done, but knowing nothing (significant) about FP. And that's fine. But personally, I think that if you want to be serious about your craft, you should learn about FP, what it is, what it's good for, how to think in it, when to use it, and when to avoid it. It's not the One True Style(tm), but it is

[0] http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html

[1] https://news.ycombinator.com/submitted?id=raganwald

[2] https://news.ycombinator.com/submitted?id=fogus

[3] http://blog.fogus.me/2011/09/08/10-technical-papers-every-pr...

[4] http://stackoverflow.com/questions/36504/why-functional-lang...

[+] tmoertel|12 years ago|reply
Why should you care about functional programming? Here are a few reasons:

1. It's different, especially from what it seems you're already doing. Therefore it is likely that if you do FP in earnest, you will learn new problem-solving techniques, many of which will prove useful elsewhere.

2. In FP, the abstraction and composition model – functions – scales down farther than do objects, modules, or compilation units. That is, functions remain practical in smaller places where objects or modules would be too expensive to insert, syntactically or semantically. In FP languages, then, you can use abstraction and composition even within the "leaves" of your logic, and that's where the bulk of your code actually lives.

3. Functions have a mathematical basis that you can draw on for intuition. In some FP languages, the mathematical parallels are so reliable that you can even calculate the code you need. Calculation can work in places where main force of intellect and the search for "aha! moments" fail.

There you go, three reasons: it's different, it not only scales up but down, and it comes with useful theory.

[+] lightblade|12 years ago|reply
Functional programming is about composing functions with functions. Just like OOP is about composing objects with objects.

FP and OOP is not in conflict with each other and you can certainly use both. FP is more in conflict with procedural programming. With procedural programming you are teaching the computer how to do things step by step (for i while i < len i++). With FP, you declare what you want to be done, and the computer figure it out on its own on how to get it done. An example would be for each loop on a collection. You are not telling the computer to increment index. You just want to iterate each element. The computer is free to spawn 20 threads to iterate the collection, so long that it accomplish the goal of your expression.

[+] marcosdumay|12 years ago|reply
Well, some toughts are easier to represent in abstract algebra. Really.

Functional programming is too generical a label to make it possible to answer your question. It may apply to anything from C-like functions to no side effect languages, but I guess most people around here use the term to mean Lisp.

If that's what you meant, Lisp'll give you a huge amount of reflexivity that you can use to change the language so it better fits your problem. In addition, Lisp has a very simple environment, what makes it the go-to language when you must embbeb an interpreter.

Now, if you were thinking about Haskell, it has a very powerfull type system that helps creating bug-fre software, and makes type-driven programming possible. It also has explicit side effects, what makes it possible for the compiler to paralelize your program for you, and a quite good optimizer (but not as good as you'll get by manually writting low level code).

And there are plenty of other languages people call functional. Each of them have some interesting aspects that may be usefull for solving a problem. Again, it's a very generic label, and a quite old one, so that all current languages already adopt functional features - in consequence it's not a very usefull label.

[+] andrewflnr|12 years ago|reply
I strongly agree that functional/expression-based code is usually a nicer way of expressing algorithms. Composing functions like map and filter feels like a better way of processing collections than loops. Higher-order functions in general were a big revelation for me. Once I read about algebraic data types, they started showing up in my pseudo-code, even before I'd programmed with them. In general, declarative relationships, such as FP lets you write, tend to be easier to grok than procedures.

For the OP, let me see if this connects: In C/C++ and others, we have the ternary operator, which lets you write conditional expressions. status = n > 9000 ? "yes" : "no"; This is pretty convenient, but annoying to read afterwards, especially if you start nesting. Functional languages let you have the same convenience without the annoyance. You can do things like this OCaml: status := if n > 9000 then"yes" else "no" Yes, OCaml lets you have mutable state if you need it.

FWIW, the "just functional" language in my head is a lot closer to OCaml than Lisp. Haskell and Lisp both add a lot of "extra" stuff that's cool but (obviously) not needed to be "functional". Not that OCaml doesn't have "extra stuff", but it's not as dominant. YMMV.

[+] klrr|12 years ago|reply
As someone who only got experience in Haskell I have to ask the same thing. Why should I use X OOP language when I can write almost anything in Haskell with ease? I tried learn programming in C and Python and even the beginner language Scheme, still I did not get programming until I learnt Haskell. Code shouldn't be abstracted as real world objects becuase code is instruction not something concrete you can touch, its information. Abstracting it using a more mathematical approuch very much makes sense and let even a beginner be able to handle quite complex problems.
[+] MichaelGG|12 years ago|reply
Higher order functions is what made it click for me. Immutability, purity and the other stuff is nice, but certainly not mandatory to use FP.

The reduction in verbosity due to easier ways to approach things is what sold FP to me. Common patterns that I'd write several times in OO are easy to abstract in FP.

Tim Sweeny, of Epic Games (Unreal Engine) says that in their C++ codebase, 90% of loops are functional folds or maps.

I imagine the biggest issue, especially as an embedded programmer, is the lack of excellent optimizing compilers that let you write functional, but still obtain the same performance.

[+] someguyperson|12 years ago|reply
As others have said, immutability and referential transparency enable you to reason about your programs much more cleanly that you are generally able to in imperative code. Functional programming also forces you to think about problems in a recursive manner, which is inherently very valuable, as the idea of solving problems by recursion is extremely powerful. Take a simple problem like this: given k different denominations of coins, how many ways can you make change for n dollars? (This is a problem from SICP, which also seems to come up on HN a lot). The functional code for this is about 10 lines. I don't even know how you would solve this imperatively...

You may look at this example and think it's contrived, that "real world problems" aren't anything like counting ways to make change. But just because you haven't encountered it in your daily work doesn't mean that other people haven't, or that it's not valuable as a tool in itself. And ultimately, having conceptual tools for thinking about problems is always going to be a good thing.

Btw, I find it slightly amusing that you don't want "to write programs like I'm writing on a Turing machine" since imperative programming maps pretty directly onto TMs as a model of computation...but I digress :)

[+] RyanZAG|12 years ago|reply
I think a lot of people gloss over a bit too quickly on the actual implications at a hardware level on what it means to have immutable state. If you have 10mb of numbers that you need to add 1 to, immutable state means you now have 20mb of numbers guaranteed. Allowing for mutable state means you can stick to the 10mb and mutate it.

So in general, forced immutable state means that any class of problems which require both high performance and a lot of mutation are going to be very poor candidates for functional programming. This is actually a large share of embedded programming and why the OP probably hasn't found that much use for it - embedded programming often comes down to accepting large amounts of data from sensors and manipulating it.

The problems where functional programming shine are where you have a chunk of data and need to create an answer from it. Given 10mb of data, should I do X or Y? Functional programming is a very good tool here because of aspects such as composability allowing for extra steps to be chained into a calculation, and immutable data ensuring that multiple steps can be run on the same data at the same time.

Hopefully that helps a bit in deciding when functional would be useful or less useful.

[+] runT1ME|12 years ago|reply
> If you have 10mb of numbers that you need to add 1 to immutable state means you now have 20mb of numbers guaranteed

You'd only have 20mb of 'numbers' if you needed to keep a reference to the original data structure. Otherwise its' garbage.

>So in general, forced immutable state means that any class of problems which require both high performance and a lot of mutation are going to be very poor candidates for functional programming

FP does not necessitate 'immutable state', even in pure languages like Haskell. What it enforces is purity, or more concrete, referential transparency. Often the easiest way to obtain referential transparency is immutable data (non destructive data structures, etc), but you can have mutable data and maintain referential transparency for high performance (see ST Monad).

[+] MichaelGG|12 years ago|reply
Something like "map myints (+ 1)" can be performed in a mutable way, without compromising on functional purity. (So long you're not using the old values afterwards, of course.)

Not all compilers perform such optimizations, but there's no fundamental conflict.

I believe Haskell's GHC compiler has "fusion" which allows it to wrap chains of maps/filters/folds into a single tight loop. That way you keep functional style, but get performance of writing it imperatively.

[+] wesnerm2|12 years ago|reply
Immutable state does not necessarily imply 20mb of numbers guaranteed. It depends on the compiler and on the implementation of data structures. Functional data structures can use mutation behind the scenes to produce a facade of immutability. Clojure has an efficient "immutable" array data structure offering constant-time (~log32 n) performance and better space usage. Shallow binding is another general technique that produces space and time performance nearly identical to corresponding mutable data structures by wrapping the mutable data structure with older versions storing a pointer to the incrementally newer version plus the incremental change. Both of these use mutation behind the scenes. It's not necessary to sacrifice performance to program in a functional way.
[+] wonderzombie|12 years ago|reply
That's a somewhat naive view of immutability. Garbage collection should take care of the 10mb of numbers you aren't using anymore. True, there are issues for more complex data structures, but that's why persistent data structures were invented. The goal is only to retain what you use.

This isn't that different from an imperative world. If someone somewhere has a reference to some part of that data (or may have a reference), what are you going to do? Presumably you'll make a copy. In an immutable world, adding references to an existing of data is trivial. So either you accrete more data, in which case you're in the same place as the imperative world, or it's not new data, no one is using it, and you can throw it out.

In practice it's probably more complex than that, and depends on the language implementation.

There's also something to be said for something which, implementation-wise, mutates in place when possible but semantically acts as if it is immutable.

I'm not advocating for FP in embedded systems, just trying to flesh out more detail about the trade-off here. I think even if you don't program with a language with immutability, it may be worth asking questions (which may have a simple answer in the affirmative!) about the extent to which you need mutability in a given situation vs not. "Don't optimize yet" and all that.

[+] yetanotherphd|12 years ago|reply
Totally agree on the hardware implementation.

Mutable state has two uses. First, when what you are modelling really has state the varies with time (e.g. real world stuff, UI stuff). Second, as a kind of optimization.

One good reason for learning FP is that you think more clearly about what mutable state you are using and why.

For example, OOP programmers tend to instinctively write

  obj.setX(x)
  obj.doG()
  y = obj.doF()
when it could be done as

  y = F(G(x))
They instinctively cache G(x) in case they need it later, even though this never actually happens.
[+] marcosdumay|12 years ago|reply
> If you have 10mb of numbers that you need to add 1 to, immutable state means you now have 20mb of numbers guaranteed.

Immutable state also means that you don't need to add 1 to the numbers you won't use, and that you can discard the old numbers if you only wanted the incremented ones.

Garbage colletors and run-time optimizers are added complexity, and may be too much for an embbebed system (or maybe not, controlers are getting bigger each day). But that's a context dependent conclusion, not a certainty.

[+] jetru|12 years ago|reply
Don't the compilers optimize this? I always believed functional programming to be popular among compiler experts because it's easier to write certain optimizations on a program because of it's many properties like state not changing etc. I don't know if this leads to realistically faster programs in real life.
[+] S4M|12 years ago|reply
I am not as experienced as you, far from it, but my -very limited - understanding of functional programming is that it's all about limiting side effects, which means limiting the ways you can screw up something. I could try to explain what I think, but John Carmack did it much better than I would ever do in a blog post, that was once submitted on HN: http://www.altdevblogaday.com/2012/04/26/functional-programm...
[+] acconrad|12 years ago|reply
Can anyone provide advice on which language to pick? I started learning C++ and Java in high school and college because those were forced by the curriculum. My first two jobs out of school forced me also to use C# which was close enough to Java. When I wanted to make the leap to script languages, my choice between Python and Ruby basically boiled down to the strength of the local Ruby / Python communities, and Ruby clearly won in that realm.

But with functional programming, I want to learn, but I have far less direction. Scala is perhaps the biggest, but still small. Haskell seems to be the most "hardcore", which appeals to me, whereas I keep anecdotally seeing people who like Ruby and Javascript (my two main languages) also tend to gravitate towards Clojure, and I can't explain why.

So far I've hated Scala's syntax and formatting (much like I hated Objective-C). Haskell is making my brain melt. Clojure seems quite simple and reminds me of my days writing Scheme in school. Any thoughts?

[+] frowaway001|12 years ago|reply
I'm a bit biased, but I'd recommend giving Scala a try. There is a lot of non-sense floating on the internet about the language, but I suggest making up your own mind.

Tooling and IDE support is better than Haskell and Clojure, so you can focus on the language instead of fighting with tools.

If you know Scala a bit, it's easy to move to something else. For instance, if you decide that you don't need types, you can move to Clojure; if you decide you don't need unchecked side-effects you can move to Haskell; if you decide that you don't need functional programming, you can move to a different OOP language (or keep using Scala).

[+] ascendantlogic|12 years ago|reply
short and sweet: Functional programming as a paradigm is "about" the following things as I understand it:

1) functions as first class citizens (this is a great read that relates to this: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...)

2) immutable data: mutable state is the enemy of threading/concurrency/parallelism. Things can change underneath you in unexpected ways causing unforeseen consquences. When the data never changes, this sort of thing becomes much easier.

3) Referential transparency: This is related to #2. Whenever you pass A into function Z, you get B. There's no chance of side effects or other possible outcomes. A goes in, B comes out.

[+] capcah|12 years ago|reply
tl;dr: State is bad for optimizations, FP encourages you to write most of the code state-free.

One of the perks of FP is that it exposes pure functions, functions that have a fixed output for a given input. Since it's really bad at storing and mutating state(canonically can only be done via monads), it encourages you to separate your code into pure and mutable states. Let's take, for instance, a function that reads a file and xor's that with a random byte stream. A non functional approach would be something like this:

  void xor_file_stream(file input, stream random_stream){
    result = []
    file_byte = read_byte(input)
    while(file_byte != EOF){
      stream_byte = read_byte(random_stream)
      res.append(file_byte^stream_byte)
      file_byte = read_byte(input)
    }return result
  }
A possible way to write the same function using FP, using currying[1] and map[2](a fundamental construct in FP), one can write the code as this:

  byte xor(byte b1, byte b2){
    return byte(byte b2){
      return b1^b2
    }
  }
  //Here I assume the language has file and random_stream as iterables
  void xor_file_stream(file input,stream random_stream){
    return map(map(xor,random_stream),input)
  }
While, in a naive implementation, this would be much slower than the procedural implementation, there are much stronger assumptions one can make in respect to the functions while optimizing the compiler. First, the function xor is a pure function with 2^9 possible input values, and can be substituted by a precomputed lookup table, speeding up the map function. Since the function is guaranteed not to hold state, one can also unroll the map loops, or paralelize it if needed.

But remember that most "pure" FP languages are bad at keeping state, turning a huge game(for instance) into a challenge both to the programmer and to the compiler.

[1] - http://en.wikipedia.org/wiki/Currying

[2] - http://en.wikipedia.org/wiki/Map_%28higher-order_function%29

EDIT: Formmating, not used to posting to hackernews comments

[+] camus2|12 years ago|reply
> Why should I care?

You dont have too,but if you like your job, you should be curious about stuff you dont know. Dont you want to learn how you could solve problems a better way?

[+] slashnull|12 years ago|reply
I would be interested in your views about OOP; it shares some ideals with FP, namely, encapsulation. OOP puts a lot of emphasis on encapsulating state, and binding the associated behavior to it so that no other part of the program can access it and cause problems, and so that contracts can be expressed by clean, abstract interfaces. It also typically uses procedural workflow and algorithmic and adds to it classes as a code organisation paradigm. OOP is boxes; stuff is in boxes so you don't see it.

FP tries to do that but with behavior, so that any given operation can be cleanly plugged into other behavior via function composition and higher-levels such as maps, folds and their math-ey equivalents functors and monads. FP is pipes. Stuff goes in, other stuff comes out, and we have an elaborate system of pipe ends to make sur one's out fits into the other's in.

OOP is good when a lot of persistent state has to hang around; objects in a game, objects floating around in a GUI window, client, transaction, product objects in a commercial application that must be created, viewed, modified, deleted. Boxes in a warehouse, bills and client data in boxes, running and shooting boxes in your Xbox.

FP is good when data has to come in, be transformed, and get out. Text treatment for example. grep is obviously made out of super-low-level procedural C, but it would be a perfect fit for the FP model: a bunch of text comes in, gets chopped into lines, a search algo is performed on each one, and it all gets spitted out, possibly into another, well, pipe. Compilers are also a nice fit: yet some more text comes in, it's chopped into a flat list of tokens, it's parsed into an abstract syntax tree, then into a type-checker, then a few other pieces of optimisation piping, and finally gets spit out as some other lower-level representation.

Server-side scripting is the most common real-life application of functional-ish langs; a request comes in, a few DB requests are made, the return values are chopped into HTML (or whatever mid-level representation then html), and that stuff is spit on the wire, which as far as it goes is isomorphic to a pipe.

Symbolic computing is a pretty sweet match for FP: some notation for an equation comes in, is turned into an expression tree, then into some standard form, then gets solved or integrated or whatever (recursive tree-walks all over the place), then turned back into a human-friendly form and spit back to the display.

Pandoc is a neat Haskell lib that takes some document format (LaTeX, HTML, markdown, whatever), turns it into a unified representation, then spits it back as another format.

JavaScript is funny because it totally embraces the fact that behavior is data, that pipes are also boxes. You give a callback to a handler, which then puts stuff like it as if were a pipe, and then you have functions that implicitly act on a global, implicit data structure (the DOM), some of them that take a description of where does one want to go in the DOM and returns a node of that recursively-defined tree, such that functions can still be chained and it's relevant to do so even if they're mostly used for their side-effects. JS is the weirdest and most wonderful thing. It even has JSON, which is the funniest example of the code-is-data ethos since LISP and it's exposed AST.

But yeah, it's in the nature of FP to not care about state, so it's normal that keeping state around in FP-styled programs is a pain. Passing a global data structure/container to a function that reaches down into the structure and returns a new, modified instance of that global and re-assigning the new into the old is just... Yuck. If a player gets killed in my game state, I want to just delete it from my player list, not replace my player list with a new one.

It's in the nature of OOP to see behavior as a second-class citizen to state. In patterns parlance, FP means using the iterator and the strategy pattern all over the place as they were themselves objects. Just like OOP lets me put two objects together and have a new one, and perhaps put an array of those inside another object. In FP, I can put two different strategies together, and have another strategy, and put that compound in the iterator pattern/strategy, and have another strategy that I can still compound. Recursion (and the oldest FP performance tweak for it, the tco) is a nice way to express iteration without the use of a dedicated iteration construct, which is totally not a nice piece of pipe; you can do function composition by nesting calls [ much(foo(grunk(one_cookie_yum))); ], but it gets weird [ much(foo(grunk_many_cookies(many_cookies_yum))); but you can't do something like {for one_cookie : many_cookies much(foo(grunk(one_cookie)))}; or you could but it would be a built-in construct which you wouldn't have control over, while in haskell you can just do map (much . foo . grunk) many_cookies ]

So yeah.

If you can picture your program like a pipe, it's probably a good fit for FP. The kind of embedded softare you do probably is not remotely looking like a pipe, so you can keep on not understanding FP. If it can provide you some comfort and perhaps a laugh, the legendary pure-functional no-side-fx-allowed the-ghc-actually-is-the-flagsip-project Haskell has a runtime made out of C.

[+] AnimalMuppet|12 years ago|reply
I asked, "When is FP the right tool for the job?"

"If you can picture your program like a pipe..." may well be the answer I was looking for. Thanks!

[+] L8D|12 years ago|reply
This is the best explanation of functional programming I've ever read.

Congrats.

[+] wturner|12 years ago|reply
This was really excellent
[+] NDizzle|12 years ago|reply
This past week I bought three books about Erlang. I'm going to be studying and going through all of those books in the next month or two.

Why? Because I have the same questions you do, only I'm a much worse programmer/developer.

Insert Nike slogan here.

What's the worst thing that can happen by learning something?

[+] AnimalMuppet|12 years ago|reply
> What's the worst thing that can happen by learning something?

If nothing else, opportunity cost. I can't use the same time to learn something else.

[+] amirouche|12 years ago|reply
> Why functional programming?

At least for the sake of thinking in another paradigm, having another «environment» to think about same issues.

I have scarce experience in functional language but AFAIK there are not only good for in->process->out like it's explained in other post.

[+] bjourne|12 years ago|reply
Because functional code is on average half the size of imperative code. The shorter the code, the better. If that argument doesn't convince you, then you need to first understand why writing short code is so important.