141

I've recently been learning about functional programming (specifically Haskell, but I've gone through tutorials on Lisp and Erlang as well). While I found the concepts very enlightening, I still don't see the practical side of the "no side effects" concept. What are the practical advantages of it? I'm trying to think in the functional mindset, but there are some situations that just seem overly complex without the ability to save state in an easy way (I don't consider Haskell's monads 'easy').

Is it worth continuing to learn Haskell (or another purely functional language) in-depth? Is functional or stateless programming actually more productive than procedural? Is it likely that I will continue to use Haskell or another functional language later, or should I learn it only for the understanding?

I care less about performance than productivity. So I'm mainly asking if I will be more productive in a functional language than a procedural/object-oriented/whatever.

Sasha Chedygov
  • 116,670
  • 26
  • 98
  • 110

8 Answers8

175

Read Functional Programming in a Nutshell.

There are lots of advantages to stateless programming, not least of which is dramatically multithreaded and concurrent code. To put it bluntly, mutable state is enemy of multithreaded code. If values are immutable by default, programmers don't need to worry about one thread mutating the value of shared state between two threads, so it eliminates a whole class of multithreading bugs related to race conditions. Since there are no race conditions, there's no reason to use locks either, so immutability eliminates another whole class of bugs related to deadlocks as well.

That's the big reason why functional programming matters, and probably the best one for jumping on the functional programming train. There are also lots of other benefits, including simplified debugging (i.e. functions are pure and do not mutate state in other parts of an application), more terse and expressive code, less boilerplate code compared to languages which are heavily dependent on design patterns, and the compiler can more aggressively optimize your code.

Dave Jarvis
  • 28,853
  • 37
  • 164
  • 291
Juliet
  • 76,873
  • 44
  • 191
  • 224
  • 5
    I second this! I believe functional programming will be used much more widely in the future because of its suitability to parallel programming. – Ray Hidayat May 10 '09 at 03:24
  • @Ray: I would also add distributed programming! – Anton Tykhyy May 10 '09 at 05:03
  • Most of that is true, except for debugging. It's generally more difficult in haskell, because you don't have a real call stack, just a pattern-match stack. And it's a lot harder to predict what your code ends up as. – hasufell Nov 10 '16 at 10:54
  • @hasufell that is compensated with the possibility of doing code less buggy in haskell. Actually my real concerns are on performance. – hdkrus Dec 20 '16 at 23:16
  • @hdkrus no, it's not. You cannot compensate for bad debugging. And you can write buggy haskell code rather easily, including state bugs. – hasufell Dec 22 '16 at 17:55
  • @hasufell maybe the quid is using functional programming to go 'stateless' as with Erlang. The thing is as by now, functional programming is perfect for processing data in stateless way and is more prone to correctness than imperative programming. – hdkrus Dec 22 '16 at 18:56
  • @hdkrus I disagree with that too. It's not about functional vs imperative, it's about "reasoning about code". That can also be achieved in an imperative language, which has a) an effect system and requires all effects to be on type level, b) uses immutability by default, c) is memory-safe. – hasufell Jan 02 '17 at 10:50
  • 3
    Also, functional programming isn't really about "stateless". Recursion is already implicit (local) state and is the main thing we do in haskell. That becomes clear once you implement a few non-trivial algorithms in idiomatic haskell (e.g. computational geometry stuff) and have fun debugging those. – hasufell Jan 02 '17 at 10:58
  • @hasfull, i didn't mean that functional programming is about "stateless". I said that programming "stateless" in Erlang is a "Goal from middle camp", also in Erlang there's not recursion, because there's not return concept in functions so there's not an implicit local state or stack data. Of course that "stateless" approach can't be used for all solutions, that's why i think that it fits for big data computations without persistence. An example of applications is in big networks computing. – hdkrus Jan 04 '17 at 04:57
  • 2
    Dislike equating stateless with FP. **Many FP programs are filled with state, it simply exists in a closure rather than an object.** – mikemaccana Jun 22 '17 at 10:53
47

The more pieces of your program are stateless, the more ways there are to put pieces together without having anything break. The power of the stateless paradigm lies not in statelessness (or purity) per se, but the ability it gives you to write powerful, reusable functions and combine them.

You can find a good tutorial with lots of examples in John Hughes's paper Why Functional Programming Matters (PDF).

You will be gobs more productive, especially if you pick a functional language that also has algebraic data types and pattern matching (Caml, SML, Haskell).

Sasha Chedygov
  • 116,670
  • 26
  • 98
  • 110
Norman Ramsey
  • 188,173
  • 57
  • 343
  • 523
  • 1
    Wouldn't mixins also provide reusable code in a similar way with OOP? Not advocating OOP just trying to understand things myself. – mikemaccana Jun 22 '17 at 10:56
20

Many of the other answers have focused on the performance (parallelism) side of functional programming, which I believe is very important. However, you did specifically ask about productivity, as in, can you program the same thing faster in a functional paradigm than in an imperative paradigm.

I actually find (from personal experience) that programming in F# matches the way I think better, and so it's easier. I think that's the biggest difference. I've programmed in both F# and C#, and there's a lot less "fighting the language" in F#, which I love. You don't have to think about the details in F#. Here's a few examples of what I've found I really enjoy.

For example, even though F# is statically typed (all types are resolved at compile time), the type inference figures out what types you have, so you don't have to say it. And if it can't figure it out, it automatically makes your function/class/whatever generic. So you never have to write any generic whatever, it's all automatic. I find that means I'm spending more time thinking about the problem and less how to implement it. In fact, whenever I come back to C#, I find I really miss this type inference, you never realise how distracting it is until you don't need to do it anymore.

Also in F#, instead of writing loops, you call functions. It's a subtle change, but significant, because you don't have to think about the loop construct anymore. For example, here's a piece of code which would go through and match something (I can't remember what, it's from a project Euler puzzle):

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

I realise that doing a filter then a map (that's a conversion of each element) in C# would be quite simple, but you have to think at a lower level. Particularly, you'd have to write the loop itself, and have your own explicit if statement, and those kinds of things. Since learning F#, I've realised I've found it easier to code in the functional way, where if you want to filter, you write "filter", and if you want to map, you write "map", instead of implementing each of the details.

I also love the |> operator, which I think separates F# from ocaml, and possibly other functional languages. It's the pipe operator, it lets you "pipe" the output of one expression into the input of another expression. It makes the code follow how I think more. Like in the code snippet above, that's saying, "take the factors sequence, filter it, then map it." It's a very high level of thinking, which you don't get in an imperative programming language because you're so busy writing the loop and if statements. It's the one thing I miss the most whenever I go into another language.

So just in general, even though I can program in both C# and F#, I find it easier to use F# because you can think at a higher level. I would argue that because the smaller details are removed from functional programming (in F# at least), that I am more productive.

Edit: I saw in one of the comments that you asked for an example of "state" in a functional programming language. F# can be written imperatively, so here's a direct example of how you can have mutable state in F#:

let mutable x = 5
for i in 1..10 do
    x <- x + i
Ray Hidayat
  • 15,219
  • 4
  • 33
  • 42
  • 1
    I agree with your post generally, but |> has nothing to do with functional programming per se. Actually, `a |> b (p1, p2)` is just syntactic sugar for `b (a, p1, p2)`. Couple this with right-associativity and you've got it. – Anton Tykhyy May 10 '09 at 05:07
  • 2
    True, I should acknowledge that probably a lot of my positive experience with F# has more to do with F# than it does with functional programming. But still, there is a strong correlation between the two, and even though things like type inference and |> aren't functional programming per se, certainly I would claim they "go with the territory." At least in general. – Ray Hidayat May 10 '09 at 11:19
  • |> is just another higher-order infix function, in this case a function-application operator. Defining your own higher-order, infix operators is *definitely* a part of functional programming (unless you're a Schemer). Haskell has its $ which is the same except information in the pipeline flows right to left. – Norman Ramsey May 10 '09 at 16:50
15

Consider all the difficult bugs you've spent a long time debugging.

Now, how many of those bugs were due to "unintended interactions" between two separate components of a program? (Nearly all threading bugs have this form: races involving writing shared data, deadlocks, ... Additionally, it is common to find libraries that have some unexpected effect on global state, or read/write the registry/environment, etc.) I would posit that at least 1 in 3 'hard bugs' fall into this category.

Now if you switch to stateless/immutable/pure programming, all those bugs go away. You are presented with some new challenges instead (e.g. when you do want different modules to interact with the environment), but in a language like Haskell, those interactions get explicitly reified into the type system, which means you can just look at the type of a function and reason about the type of interactions it can have with the rest of the program.

That's the big win from 'immutability' IMO. In an ideal world, we'd all design terrific APIs and even when things were mutable, effects would be local and well-documented and 'unexpected' interactions would be kept to a minimum. In the real world, there are lots of APIs that interact with global state in myriad ways, and these are the source of the most pernicious bugs. Aspiring to statelessness is aspiring to be rid of unintended/implicit/behind-the-scenes interactions among components.

Brian
  • 113,581
  • 16
  • 227
  • 296
  • 6
    Someone once said that overwriting a mutable value means that you are explicitly garbage collecting/freeing the previous value. In some cases other parts of the program weren't done using that value. When values cannot be mutated, this class of bugs also goes away. – shapr May 11 '09 at 17:05
8

One advantage of stateless functions is that they permit precalculation or caching of the function's return values. Even some C compilers allow you to explicitly mark functions as stateless to improve their optimisability. As many others have noted, stateless functions are much easier to parallelise.

But efficiency is not the only concern. A pure function is easier to test and debug since anything that affects it is explicitly stated. And when programming in a functional language, one gets in the habit of making as few functions "dirty" (with I/O, etc.) as possible. Separating out the stateful stuff this way is a good way to design programs, even in not-so-functional languages.

Functional languages can take a while to "get", and it's difficult to explain to someone who hasn't gone through that process. But most people who persist long enough finally realise that the fuss is worth it, even if they don't end up using functional languages much.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Artelius
  • 45,612
  • 11
  • 85
  • 102
  • That first part is a really interesting point, I'd never thought about that before. Thanks! – Sasha Chedygov May 10 '09 at 02:52
  • Suppose you have `sin(PI/3)` in your code, where PI is a constant, the compiler could evaluate this function *at compile time* and embed the result in the generated code. – Artelius Nov 02 '09 at 03:40
6

Stateless web applications are essential when you start having higher traffic.

There could be plenty of user data that you don't want to store on the client side for security reasons for example. In this case you need to store it server-side. You could use the web applications default session but if you have more than one instance of the application you will need to make sure that each user is always directed to the same instance.

Load balancers often have the ability to have 'sticky sessions' where the load balancer some how knows which server to send the users request to. This is not ideal though, for example it means every time you restart your web application, all connected users will lose their session.

A better approach is to store the session behind the web servers in some sort of data store, these days there are loads of great nosql products available for this (redis, mongo, elasticsearch, memcached). This way the web servers are stateless but you still have state server-side and the availability of this state can be managed by choosing the right datastore setup. These data stores usually have great redundancy so it should almost always be possible to make changes to your web application and even the data store without impacting the users.

shmish111
  • 3,246
  • 4
  • 24
  • 46
6

Without state, it is very easy to automatically parallelize your code (as CPUs are made with more and more cores this is very important).

Zifre
  • 24,944
  • 8
  • 81
  • 102
  • Yes, I've definitely looked into that. Erlang's concurrency model in particular is very intriguing. However, at this point I don't really care about concurrency as much as productivity. Is there a productivity bonus from programming without state? – Sasha Chedygov May 10 '09 at 02:16
  • 2
    @musicfreak , no there isn't a productivity bonus. But as a note, modern FP languages still let you use state if you really need it. – Unknown May 10 '09 at 02:22
  • Really? Can you give an example of state in a functional language, just so I can see how it's done? – Sasha Chedygov May 10 '09 at 02:31
  • Check out the State Monad in Haskell - http://book.realworldhaskell.org/read/monads.html#x_NZ – rampion May 10 '09 at 05:17
  • 4
    @Unknown: I disagree. Programming without state reduces the occurence of bugs that are due to unforeseen/unintended interactions of different components. It also encourages better design (more reusability, separation of mechanism and policy, and that sort of stuff). It's not always appropriate for the task at hand but in some cases really shines. – Artelius Nov 02 '09 at 04:47
5

I wrote a post on just this subject awhile back: On The Importance of Purity.

naasking
  • 2,388
  • 1
  • 25
  • 30