⬆️ ⬇️

Ramda Thinking: First Steps

Recently, I met a wonderful series of articles " Thinking in Ramda " that clarify with simple and clear examples how to write code in a functional style using the Ramda library. These articles seemed so wonderful to me that I could not resist translating them into Russian. I hope that this will be useful for many people :) Let's start the translation from the first introductory article.



1. First steps

2. We combine functions

3. Partial application (currying)

4. Declarative programming

5. Ruleless Notation

6. Immutability and objects

7. Immutability and arrays

8. Lenses

9. Conclusion



This post is the start of the Ramda Thinking series on functional programming.

')

I will use the Ramda library in these articles, although many of the ideas discussed are also applicable to many other libraries, such as Underscore and Lodash, as well as to other programming languages.



I will stick with the easy, less academic side of functional programming. This is mainly because I want the series to be accessible to more people, but also partly because I myself am not so close to a truly functional way.



Ramda



I’ve mentioned the Ramda JavaScript library on this blog several times:



“In Using Ramda with Redux ” (I hope to also translate this article later), I showed some examples of how Ramda can be used in various contexts when writing a Redux application.



- In " Using Redux-api-middleware with Rails ", I used Ramda to transform the payload to requests and returned answers.



I found Ramda a beautifully designed library that provides many tools for pure and elegant functional JavaScript programming.



If you want to experiment with Ramda in the process of reading this series of articles, then the Ramda site has a handy browser sandbox for your experiments.



Functions



As the name suggests, functional programming has much in common with functions. For our situation, we define a function as a piece of code being reused, which is called with the number of arguments equal to zero or more, and returns the result.



This is a simple JavaScript function:



function double(x) { return x * 2 } 


Together with the switch functions from ES6, you can write the same function much more briefly. I mention this now, because we will use a lot of switch functions as our articles progress.



 const double = x => x * 2 


Some languages ​​go further and provide support for functions as first-class constructs. By “first class constructs,” I mean that functions can be used in the same way as other values. For example, you can:



- refer to them in constants and variables

- pass them as parameters to other functions

- return them as a result from other functions.



JavaScript is one of these languages, and we will use this advantage.



Pure functions



When writing functional programs, you eventually come to understand the importance of working with so-called "clean" functions.



Pure functions are functions that have no side effects. They do not assign external variables, they do not destroy the input data, do not generate output, do not read or write to the database, they do not change the parameters that were passed to them, and so on.



The basic idea is that if you call a function with the same parameters over and over again, you will always get the same result.



Of course, you can do various things with unclean functions (and should, if your program does something interesting), but for most of the code you want to keep your functions clean.



Immutability



( or "Immunity", as often expressed by php'shniki - approx. Lane. )



Another important concept in functional programming is immobility. What does it mean? "Immunable" means "unchangeable."



When I work with immunity, after the initial initialization of a value or object, I no longer change them again. This means that you do not change the elements in the array or the properties of objects.



If I need to change something in an array or object, I return a new copy of it with modified values. In subsequent posts we will talk about this in detail.



Immunity goes hand in hand with pure functions. Since pure functions do not have the right to create side effects, they do not have the right to change external data structures. They are forced to work with data in an immutable style.



Where to begin?



The easiest way to start thinking in the functional paradigm is to start replacing cycles with iterative functions.



If you come from another language that has these functions (Ruby and Smalltalk are just two examples), you may already be familiar with them.



Martin Flower has a collection of excellent articles about “Collection Flows” that show how to use these functions and how to refactor existing code into collection processing flows .



Note that all of these functions (except for reject) are available in Array.prototype, so you don’t need Ramda to start using them. However, I will use the Ramda version for consistency with the rest of the articles.



forEach



Instead of writing an explicit loop, try using the forEach function instead. Like this:



 //  : for (const value of myArray) { console.log(value) } //  : forEach(value => console.log(value), myArray) 


forEach takes a function and an array, and calls this function to each element of the array.



While forEach is the most accessible of these functions, it is least used when performing functional programming. It does not return values, so it is in reality used only for calling functions that have side effects.



map



The next most important function we will study is the map. Like forEach, map applies a function to each element of the array. However, unlike forEach, map collects the result of applying this function to a new array and returns it.



Here is an example:



 map(x => x * 2, [1, 2, 3]) // --> [2, 4, 6] 


It uses an anonymous function, but we can use a named function here:



 const double = x => x * 2 map(double, [1, 2, 3]) 




filter / reject



Now, let's take a look at filter and reject. As the name implies, filter selects elements from an array, based on some function. Here is an example:



 const isEven = x => x % 2 === 0 filter(isEven, [1, 2, 3, 4]) // --> [2, 4] 


filter applies this function (isEven in this case) to each element of the array. Whenever a function returns a “true” value, the corresponding element is included in the result. And also, whenever a function returns a “false” value, the corresponding element is excluded (filtered) from the array.



The reject does the exact same thing, but in the opposite sense. It stores an element for each function that returns a false value, and excludes the element for those functions that return a true value.



 reject(isEven, [1, 2, 3, 4]) // --> [1, 3] 


find



find applies a function to each element of the array and returns the first element for which the function returns a true value.



 find(isEven, [1, 2, 3, 4]) // --> 2 




reduce



reduce is a bit more complex than the other functions we have reviewed today. It is worth knowing, but if you have problems understanding the essence of her work, do not let this stop you. You can go quite a long way without even understanding the essence of her work.



reduce takes a function with two arguments, the initial value and an array to work with it.



The first argument that will be passed to the function is called the “accumulator”, and the second argument is the value of the array to be iterated. The function should return the new value of the “battery”.



Let's take a look at an example and then analyze what happens in it:



 const add = (accum, value) => accum + value reduce(add, 5, [1, 2, 3, 4]) // --> 15 


  1. The reduce function calls the (add) function with the initial value (5) on the first element of the array (1). add returns the new battery value (5 + 1 = 6).
  2. reduce again calls add, this is the time of the new value of the battery (6), and the next value of the array (2). add returns 8.
  3. reduce causes add again with 8 and the next value (3), the result is 11.
  4. reduce causes add last with 11 and the last value of the array (4), the result is 15.
  5. reduce returns the final accumulated value as a result (15)




Conclusion



Starting from these iterative functions, you can grasp the idea of ​​forwarding functions to other functions. It is even possible that you have already used it in other languages ​​without understanding that you were engaged in functional programming at that moment.



In the next series



The next post in this series, “ Combining functions, ” will show how we can move on to the next step and start combining functions in new, interesting ways.

Source: https://habr.com/ru/post/348868/



All Articles