loading...

Functional Programming: The Basics

alhasenzahl profile image Amanda Hasenzahl Updated on ・8 min read

In computer science, functional programming is a programming paradigm -- a way of thinking about software construction based on a set of fundamental, defining principles.

The fundamental, defining principles that make up this paradigm are that the code:

  • follows a declarative pattern
  • is composed of pure functions
  • avoids shared state, mutable data, and side effects

Imperative vs Declarative Pattern

Imperative Pattern

When the computer is given specific steps in order to achieve a desired result -- telling the computer exactly HOW to do something.

This tends to be the pattern that developers follow most often. It is the way that, we as humans, are used to trying to solve a problem.

Declarative Pattern

When the computer is given instructions on what result is desired without telling it exactly how it is to be done -- telling the computer WHAT needs to be done.

This is the way that functional programmers approach solving a problem. They focus on what results they need, rather than how the results are achieved. It is a different approach that can be hard to adopt at first, but can do significant things for your code.

Using a for loop and the imperative pattern vs. using Array.prototype.map() and the declarative pattern<br>

Both of these examples are adding new items onto each book object inside the books array.

The for loop example (Imperative Pattern):

  1. It’s checking array index counter against the array length
  2. Adding a lastRead property to the books object with the current date as the value for the currently indexed book.
  3. Incrementing the index counter for every time through the loop

It’s giving the computer a step by step instruction for how to add these new items

The .map() example (Declarative Pattern):

  1. Takes a function as an argument
  2. That function receives each item as a parameter
  3. Adds a lastReadBy property to each book with a string value of 'me'.

It’s giving the computer the information to produce the desired result, but it is not telling it exactly how to do it. The .map() method behind the scenes is taking care of the actual operation.

Pure Functions

  • accept at least one parameter
  • return something as a result
  • return the same output if given the same input
  • produce no side effects
  • are referentially transparent -- you can replace the function call with its resulting value without changing the meaning of the program

They are also simple and reusable building blocks for your code, completely independent from outside state therefore immune to state related bugs, as well as being easy to move around, refactor, and reorganize within your code. Thus making your overall program more flexible and adaptable to future changes.

Pure function that returns the sum of two given parameters

This is an example of a pure function. It accepts at least one parameter and returns a value. When it's given the values of 3 and 5, it will always return the output value of 8. It produces no side effects because the function relies on nothing except its input values.

A pure function call being replaced by its output as an example of referential transparency

This example shows a pure function and more specifically how they can be referentially transparent.

The add(x, y) function is taking in two values and producing their added sum as an output, which in this case is 8. Then, we have the multiply(a, b) function that is also taking in two values, but this time is producing their multiplied total as an output.

Using both functions we could write this function call as the first call multiply(2, add(3, 5));. Which would first add 3 to 5, producing the sum of 8. That sum of 8 would be passed as a parameter to multiply() along with 2, to produce the value of 16 as the final output.

We could also change the add(3, 5) function call as a parameter to just the value of its output (8). This change still produces the output value of 16. This replacement didn’t affect the output of the function in anyway, which makes it referentially transparent.

Immutability and Side Effects

Immutability

When an object cannot be modified in any way after it has been created.

The goal is to keep state and data from being shared or altered and solely keep it within the scope of each function, when possible.

There are no variable or loops, at least not how we are used to seeing them. Stored values are called variables because of history, but they are constants. Once x takes on a value, it is that value for life. They are usually local variables, so their lives are usually short, but while it is alive it can never change. Loops, on the other hand, happen through recursion.

One thing to keep in mind is that in JavaScript, the variable const doesn't necessarily equal immutability. const creates a variable name binding which cannot be reassigned after creation, however, it doesn't create immutable objects. You can't change the object that the binding refers to, but you can still change the properties of the object. This ultimately means that bindings created with const are mutable.

Recursion is when a function calls or refers to itself. This is used in place of traditional loops. Old values aren't modified during the looping, instead recursion uses new values calculated from the old ones. This allows constants and data to be modified as little as possible.

Flip book of a meteor striking Earth and killing the dinosaurs

Recursion is like a flip book. Each instance would be like each individual page of the flip book. They are completely independent of each other, don't modify anything on any of the other pages, and putting each instance together gives you the final result.

Blocks moving across a conveyer belt on an assembly line

Traditional loops are more like an assembly line. Each part of the process molds or changes the object until you get the final result. Each part is reliant on the one that comes before and after it and the final result is reliant on each part of the process and the order in which they are completed in.

A function that finds factorials using recursion

There are three key features in a recursion function.

  1. Termination Case
    It stops the function from happening infinitely. It is the emergency brake and is used to break out of the logic if you have reached the end of the input or if there is a bad input and you don’t want the code to run at all (in this example a negative number because there aren’t factorials for negative numbers). The termination case for this example is x < 0.

  2. Base Case
    Similar to the termination case, it is also used to stop the recursion from continuing. Base case however, is the goal of the function. In this example, x === 0 is the base case because once x has gotten down to 0, the factorial has been found and the recursion doesn’t need to go any further.

NOTE: In some functions, you will only see a base case instead of a base case and a termination case. This would be because the base case and the termination case are the same or can be taken care of in one single call.

  1. Recursion The function repeatedly calling itself until it reaches its base case. In this example, that is return x * factorial(x - 1);.

Step by step breakdown of the factorials recursion function with 3 as an input

This example breaks down as follows:

  1. We are calling the function and passing it the value of 3 → factorial(3);
  2. The function is run and since 3 is greater than 0, the function returns 3 * factorial(3-1) OR 3 * factorial(2)
  3. The function is run again with the value of 2 → factorial(2);
  4. Again 2 is greater than 0, so the function returns 2 * factorial(2-1) OR 2 * factorial(1)
  5. The function is then run again with the value of 1 → factorial(1);
  6. Once again it is greater than 0, so the function returns 1 * factorial(1-1) OR 1 * factorial(0)
  7. When the function is run another time with the value of 0, the base case becomes true, so the function returns the value of 1 (if (x === 0) return 1)
  8. Now that the function has finally finished, everything unwinds.
  9. IMPORTANT -- Recursion is a group of nested function calls, so the innermost function will return first (Last One In, First One Out)
  10. Everything unwinds in the order shown at bottom of the image above

Side Effects

Any application state changes that are observable outside the called function other than its return value.

Elements in your code that can cause side effects are:

  • modifying any external variable or object property
  • logging to the console
  • writing to the screen, a file, or the network
  • triggering any external process
  • calling other functions that contain side effects

Unfortunately, you can’t have a program or code base that is completely 100% free from side effects, but you can work to keep them contained and isolated within your code. This makes it easier to extend, refactor, debug, test, and maintain your code. It is also why front end frameworks encourage users to manage state and component renderings in separate, loosely coupled modules.

Shared State is something that will create side effects within your code if it is altered.

One reason for this is because it is impossible to know the entire history of every shared variable, especially if there are asynchronous calls happening within your code.

An example of this would be if there was a user object for your program that needed to be saved. The saveUser() function makes a request to the API on the server and while that is happening, the user changes their profile picture with the updateAvatar() function. This triggers a second request with saveUser(). Since these are async calls, if the second call is received first, when the first call (now outdated) call gets returned, the new profile picture will get deleted and replaced with the old one.

This is an example of a race condition, which is a common bug with having shared state. During that entire process there are times when you don't know what's happening to the user object. Therefore, sometimes you receive a result you weren't expecting.

Another reason is because when the order of the functions changes or they get moved around it causes a cascade of failures within your code.

Two functions with the same values and operations result in two different outputs when executed in a different order

The first half of this example is taking the value in x and first executing the x1() function which adds 1 to make x.val = 3. Then it is executing x2() which is multiplying that by 2 to make x.val = 6.

The second half is the exact same values and functions as the first, however the two functions get called in reverse. It starts with the value of 2, then it multiplies that by 2 to get 4, and then it adds 1 to that. This gives you a final result of 5.

Changing the order of the function calls on the exact same value, produced two different resulting values.

Summary

  1. Functional programming is a way to approach solving software challenges based on a set of fundamental, defining principles: follows a declarative pattern, utilizes pure functions, and avoids using shared state, mutable data, as well as creating side effects.
  2. The declarative pattern entails giving the computer what you are wanting as a result without telling it exactly how it needs to be done.
  3. Pure functions are simple reusable blocks of code that are completely independent from any outside state. They are immune to bugs related to state changes and help make your code flexible to future changes because they are easy to move around and refactor.
  4. Shared state, mutable data, and side effects are avoided as much as possible. Although, a program can never be completely free of side effects, the goal is to keep them contained and isolated inside your code.
  5. Adopting a functional programming approach in the right situations has potential to take your code to the next level

Discussion

pic
Editor guide
Collapse
jcubic profile image
Info Comment marked as low quality/non-constructive by the community. View code of conduct
Jakub T. Jankiewicz

You're completely wrong about higher order functions:
this is example of higher order function:

function curry(fn, a) {
   return function(b) {
       return fn(a, b);
   };   
}
Enter fullscreen mode Exit fullscreen mode

this is simple example, with your code you just call two functions you don't return anything you don't use them as arguments. here fn is parameter that is used inside and function is returned. This is how this is done.

I've only skimmed through the article but it lack fundamental issues. Like function composition, example:

function add(a, b) {
   return a + b;
}
function square(a) {
   return a * a;
}
function compose(f, g) {
    return function(a, b) {
      return g(f(a, b));
    };
}   
var square_sum = compose(add, square);
square_sum(10, 20);
// 900 == square(10+30)
Enter fullscreen mode Exit fullscreen mode

I hope this will make you learn something and I hope that you will improve the article.

Collapse
alhasenzahl profile image
Amanda Hasenzahl Author

I appreciate you taking the time to comment and give some feedback. The examples that I used were all examples that I found in other articles when I was originally researching and learning about the topic for an internal talk at work (which I then adapted into this article), none of them were mine. If you have more articles or resources on the topic that go along with your comments above, I am more than happy to read them.

Collapse
jcubic profile image
Jakub T. Jankiewicz

I would read articles by Erick Elliot on Medium:
medium.com/javascript-scene/master...

Especially "What is Function Composition?"

medium.com/javascript-scene/master...

Also you can check simple example of Higher order function at wikipedia:
en.wikipedia.org/wiki/Higher-order...

Your article is not bad but higher order function section need to be rewritten, the problem is that people will read your article and learn something that is not true, this is really bad for people that just staring out.

Thread Thread
alhasenzahl profile image
Amanda Hasenzahl Author

I appreciate you providing these articles. It was and is never my intention to provide misinformation on these or any other topics in my writing. I will look these over and make appropriate changes.

Thread Thread
jcubic profile image
Jakub T. Jankiewicz

I know that you didn't make this intentional, you simply didn't know. Sorry if my first comment look like hate comment or something, my intention was to make the article better and if you would learn something in a process it would be even better. I know that this was probably lot of work to create such long article, I know because I've written few articles like that myself. I suggest taking a time and research a little bit more. If you would got Wikipedia article about higher order function, you will notice that this is not what you though, what those functions are and that you need to learn more before you write about it. Consider my first comment as Code Review or Peer Review if you will, if you don't know what CR is you can read about this on Wikipedia en.wikipedia.org/wiki/Code_review

Thread Thread
alhasenzahl profile image
Amanda Hasenzahl Author

I have participated in many code reviews before and am always open to any critiques that are offered in a constructive manner. I have removed the Higher Order Function/Function Composition section completely and will add it back in once I have had the time to go over the material more.

Thread Thread
brodwolfsky profile image
Brodwolfsky

I haven't seen your high order examples, because you've removed, but what Jakub T. Jankiewicz describes here is correct. I'm using Javascript for the Lambda Calculus. So the programming with pure functions and that only works with high-order functions. My GitBook about that (it's in German):
mattwolf-corporation.gitbook.io/ip...

I really recommend every JavaScript developer to read the articles of Erick Elliot, as Jakub T. Jankiewicz points out in the post above. The book is also very groundbreaking.

But a very another inspiring source is also Glebec ( github.com/glebec/lambda-talk )
I really really recommend to watch this:
Fundamentals of Lambda Calculus & Functional Programming in JavaScript:
youtu.be/3VQ382QG-y4

Collapse
iwi4a profile image
Ivelin Iliev

Great article, I enjoyed reading it a lot! I saw that you are after a bit more info and I would suggest Kyle's book - Functional-Light-JS. He has provided it in github or you can find it as a hard copy in amazon any many book stores. He also has a video course on that topic with FrontEndMasters.com, but that requires a subscription.

github.com/getify/Functional-Light-JS