Over a million developers have joined DZone.

Functional Programming: The Good and the Bad

DZone's Guide to

Functional Programming: The Good and the Bad

Functional programming is so hot right now! Find out how you can do it right.

· Java Zone
Free Resource

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

Functional programming is becoming more popular today. We have more and more languages that support functional programming styles, and people are learning how to use them. It's no longer as esoteric as it once was—Ruby, Java, and JavaScript all use functional programming concepts today.

These languages have functional features, but are not functional languages. In my experience, functional languages like Erlang or ML have other features that lend a certain safety to programming that mainstream languages don't. One of these is the use of recursion and argument pattern matching to control program looping. You can use these kinds of constructs for flow control as well. Another is taking constant assignment seriously. I say constant assignment here because in these languages once you've bound a value to a variable, that variable is permanently bound to that value until it falls out of scope. The downside to these kinds of features is the difficulty in learning how to develop software using them. For those of us who have grown up using imperative languages, it's a hard transition.

Recursion and Pattern Matching

Functional programming languages feature runtimes optimized for recursion. Using tail call elimination, these runtimes are able to produce recusion environments that are as efficient as looping by reusing the same stack frame for each recursive call. This, coupled with argument pattern matching, allows you to express functions in the same way you would an inductive proof. You have a basis step and an inductive step. The basis step terminates the recursion, while the inductive step continues the recursion. This way, you can define functions that naturally apply to list or collection processing. Each variable in the functions can also only be bound once per call, making variable assignment easier to manage. A pseudocode example would look something like this:

looper(_) := return 0
looper(h, t) := return t + looper(h)

Here, we define a function looper(.) to sum the contents of a list. The first step is the basis step—if the list is empty, we return 0. The second step is the inductive step—if the list has a head element and a tail element, then we add the tail element to the result of a recursive callback to looper(.). If there's only one element left in the list, that element is assigned to t, the recursive call maps to the basis step (as h is empty), and the recursive structure unwinds.

In a property functional language, tail call elimination guarantees stack frame reuse, so this essentially maps to the same structure as a for or while loop in, say, C.

If you're going to use something like this approach in Ruby or JavaScript, where you loop over a list with a function, you need to make sure that tail call elimination is available. If it isn't, you're going to take a performance hit as you recurse. And you can do this in Ruby or Javascript—you simply move the basis step to the top of the induction step definition.

Constant Assignment

This is very difficult to get used to in functional languages. Maintaining volatile state is pretty tough if the values you use to contain that state can't change, after all. So how do you work around this?

Well, remember, assigned variables are only constant in the current scope. So how do you get around this? You keep the scope small, and bind the needed variables at function invocation. You can't write code that changes state during, say, a sequence of loops. So instead, write code that binds state at function invocation, and recurse. This way, you can maintain changing state, and do so in a way that makes it more difficult to mistakenly assign incorrect values to state variables.

And don't use global variables. Tough to get those to fall out of scope.

That's two of the key differences you'll see in true functional languages, rather than languages that have bolted on functional programming capabilities. Functional programming techniques can give you a new level of reuse, and make programs more clear, but at a potential cost in non-optimized environments.

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

functional programming

Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}