DEV Community


Explain functional programming to me like I'm five

fultonbrowne profile image Fulton Browne ・1 min read


Editor guide
ahferroin7 profile image
Austin S. Hemmelgarn

In it’s purest sense, functional programming comes down to one simple principle:

‘Everything is a function.’

Functions are, of course, functions. Variables are functions that return their value. Modules are functions that return their contents. This has a couple of interesting implications that lead logically to many of the other things often associated with functional programming, namely:

  • ‘methods’ as a concept don’t really exist. Unlike OOP languages where you get some special handling that hands you the value the method is being called on in some way inherent to invoking the method, functional languages require whatever data is being operated on to be explicitly passed into the function. For example, the builtin functions map() and reduce() in Python both use calling conventions that would be seen in many functional languages, explicitly taking the iterable/enumerable that they will be operating on as their first argument. This, in turn, lends itself easily to dependency-injection design patterns, which makes testing and interoperability much easier.
  • As a general rule, once defined a function cannot be modified without completely redefining it (essentially, you can’t modify the code of a function in-place). Essentially, functions are inherently immutable once defined. Thus, because everything is a function, all data is immutable in (most) functional languages. This has a number of secondary benefits, one of the biggest ones being that it means that passing data around becomes a zero-copy operation (because the data can’t change out from under you, it’s perfectly safe to just pass references to the data around instead of the data itself. This also means that values passed into a function cannot be modified by the function.
  • Because everything is a function, it must be possible for functions to receive other functions as arguments, and return other functions as parameters. This lets you do all kinds of interesting things, such as writing functions that produce new functions based on their arguments, or wrap other functions to do special argument handling (such as currying and memoization). This is something that many non-functional languages have picked up as well (see for example how functions in Python are actually objects, and thus can be passed around like anything else), and is an important requirement for implementing the concept of decorators.

In addition, there are a number of other things often associated with functional languages that follow logically from functions being such a core part of the language but aren’t a strict requirement for the language to be functional, with the two biggest ones being

  • Special significance and focus is placed on pure functions. A ‘pure’ function is one which has exactly zero side-effects other than transforming the arguments into the return value. Pure functions are inherently thread-safe, can be eliminated from the final machine code safely if their return value is not used, allow for caching of results of complex computations that they do, etc.
  • Recursion is the preferred method of iterative evaluation. Most functional languages provide a special optimization to handle this known as ‘tail-call optimization’. The exact meaning of this isn’t entirely relevant, but languages which do this allow for a theoretically infinite number of sequential function calls provided the functions are structured correctly. Further, in cases where recursion is not possible, the preferred structure of a loop is that the loop body is a function body which takes the loop parameters as arguments (see for example the Enum.each/2 function from Elixir, it takes an enumerable and a function, and then evaluates that function for each item in the enumerable).
vampiire profile image

this was a fantastic answer. maybe a bit beyond 5 but well worth it. with a few code snippet examples this would be great as it’s own post.

vonheikemen profile image

Functional programming is a lot like playing with legos. You grab one lego, connect it with another lego and the result is yet another lego that can potentially be connected with other legos. It's legos all the way down.

To me functional programming is programming with pure functions. Use pure functions as much as you can. A lot of fancy patterns exists only to enable you to do "impure" things while still being technically pure (which is the best kind of pure).

To finish I'll just leave this right here.
Anjana Vakil — Functional Programming in JS: What? Why? How?
Scott Wlaschin - The Power of Composition

Okay, one last thing. I've documented everything I know about functional programming here on DEV, if anyone is interested you can find a guide here.

vampiire profile image

this is as 5 as i can get thinking about the practical aspects of FP (not all the theory)! let’s call each function an action.

each action has one distinct purpose.

each action can only work on the things you give it - it is blind to anything outside of it. meaning it can’t see or change anything outside the action.

if you put the same thing into an action you get the same thing out every time.

if you want more complex action you put in multiple actions into a composing action that performs them for you.

if you want an action that is pre-configured you call a pre-configuring action and give it some configuration information. it then gives you the pre-configured action back (with the configuration info saved in it) for you to use.

if you want to perform many sequential actions you can coordinate the output of one action as the input of the next action to get from the start to the finish one action at a time.

jmfayard profile image
Jean-Michel Fayard 🇫🇷🇩🇪🇬🇧🇪🇸🇨🇴

I agree with what the others said about pure functions and immutability and will extend it a bit.

If you want to bring functional programming to the real world,
there is a great pattern you should be aware of :
Functional Core, Imperative Shell
where you put your business logic in easily testable pure functions and have a thin imperative layer that is responsible for doing the side effects.

Purely functional code makes some things easier to understand: because values don't change, you can call functions and know that only their return value matters—they don't change anything outside themselves. But this makes many real-world applications difficult: how do you write to a database, or to the screen?
In this screencast we look at one method for crossing this divide. We review a Twitter client whose core is functional: managing tweets, syncing timelines to incoming Twitter API data, remembering cursor positions within the tweet list, and rendering tweets to text for display. This functional core is surrounded by a shell of imperative code: it manipulates stdin, stdout, the database, and the network, all based on values produced by the functional core.
This design has many nice side effects. For example, testing the functional pieces is very easy, and it often naturally allows isolated testing with no test doubles. It also leads to an imperative shell with few conditionals, making reasoning about the program's state over time much easier.

Do watch the screencast =>

adnanbabakan profile image
thorstenhirsch profile image
Thorsten Hirsch

🤣 🤣 🤣
I think this is the exact opposite of a "like I'm 5" answer.

adnanbabakan profile image