How to use pointfree style in JavaScript without loss of readability?

When I tried to write pointfree style JavaScript, I found that if you force every function in this style, you sometimes lose your readability. For instance:

import R from 'ramda' const ceil = Math.ceil const pagination = { total: 101, itemsPerPage: 10, currentPage: 1 } // ================= Pointful style ================== const pageCount = (pagination) => { const pages = ceil(pagination.total / pagination.itemsPerPage) const remainPages = pagination.total % pagination.itemsPerPage === 0 ? 0 : 1 return pages + remainPages } pageCount(pagination) // => 11 // ================ Pointfree style ================== const getPages = R.pipe( R.converge(R.divide, [R.prop('total'), R.prop('itemsPerPage')]), ceil ) const getRemainPages = R.ifElse( R.pipe( R.converge(R.modulo, [R.prop('total'), R.prop('itemsPerPage')]), R.equals(0) ), R.always(0), R.always(1) ) const pageCount2 = R.converge(R.add, [ getPages, getRemainPages ]) pageCount2(pagination) // => 11 

I wrote a simple pagination module to calculate pageCount to calculate the total number of items and the number of elements on a page in a dot style and a pointless style. The dot style seems to be much more readable than the dot style version. The latter looks unclear.

Am I doing it right? Is there a way to make free-style code more understandable?

+5
source share
3 answers

Hand composition

Let's start by manually creating functions:

 const calcPages = (totalItems, itemsPerPage) => ceil(div(totalItems, itemsPerPage)); const div = (x, y) => x / y; const ceil = Math.ceil; const pagination = { total: 101, itemsPerPage: 10, currentPage: 1 } console.log( calcPages(pagination.total, pagination.itemsPerPage) ); 

Software composition

In the next step, we discard the parameters:

 const comp2 = (f, g) => (x, y) => f(g(x, y)); const div = (x, y) => x / y; const ceil = Math.ceil; const calcPages = comp2(ceil, div); const pagination = { total: 101, itemsPerPage: 10, currentPage: 1 } console.log( calcPages(pagination.total, pagination.itemsPerPage) ); 

Defining a function now makes no sense. But the call code is not. If you know how the higher-order function comp2 , the expression comp2(ceil, div) pretty declarative for you.

Now it’s obvious that calcPages is the wrong name because the composition of the function is much more general. Let me call it ... intDiv (well, maybe the best name, but I suck in math).

Fracture modifier

In the next step, we modify intDiv so that it can process objects:

 const destruct2 = (x, y) => f => ({[x]:a, [y]:b}) => f(a, b); const comp2 = (f, g) => (x, y) => f(g(x, y)); const div = (x, y) => x / y; const ceil = Math.ceil; const intDiv = comp2(ceil, div); const calcPages = destruct2("total", "itemsPerPage") (intDiv); const pagination = { total: 101, itemsPerPage: 10, currentPage: 1 } console.log( calcPages(pagination) ); 

I called the modified calcPages function calcPages because it now expects a specific pagination object and therefore is less general.

If you know how the involved functions of a higher order work, everything is declarative and well read, although it is written in the style without links.

Conclusion

A dotless style is the result of a function, currying, and higher order functions. This is not in itself. If you stop using these tools to avoid a contactless style, you lose a lot of the expressiveness and elegance that functional programming provides.

+4
source

Let's start with a simple example:

 // inc :: Number -> Number const inc = R.add(1); 

I find higher clearer than its "point" equivalent:

 // inc :: Number -> Number const inc = n => R.add(1)(n); 

The lambda in the above line is just noise when one of them is convenient for partial application of Ramda functions.

Go the other way:

 // y :: Number const y = R.ifElse(R.lt(R.__, 0), R.always(0), Math.sqrt)(x); 

This would be much more clearly written in the "dot" style:

 // y :: Number const y = x < 0 ? 0 : Math.sqrt(x); 

My suggestion is to use simple expressions in simple cases and revert to a “point” style when the expression becomes confusing. I quite often went too far and then canceled the last few changes to return to a clearer expression.

+3
source

In my opinion, the problem here is "readability", more specifically, that you cannot read the code continuously from left to right in the form of text from a book.

Some functions in the ramda library can be read from right to left, for example compose() :

 import { compose, add, multiply } from 'ramda' // the flow of the function is from right to left const fn = compose(add(10), multiply) // (a, b) => (a * b) + 10 

Then you get to the point where certain functions require you to specify your parameters in a certain order (non-commutative functions), often in order from left to right, for example lt() .

 import { __, lt } from 'ramda' // you need to read the parameters from left to right to understand it meaning const isNegative = lt(__, 0) // n => n < 0 

When these changes in direction occur, then it’s more difficult for you to read the code, because it takes more effort to find the path where the code flow occurs.

 import { compose, length, gte, __ } from 'ramda' // 1) you start reading the code from left to right // 2) you see compose: switch modes to right to left, jump to the end of compose // 3) you get to gte, where you need to switch back to left to right mode // 4) glue all previous parts together in your mind, hope you'll still remember where you started const hasAtLeast3Items = compose(gte(__, 3), length) // arr => arr.length >= 3 

There are several features that are really useful when working with ramda, but can instantly damage readability. Those who require you to switch reading directions too many times, requiring you to track too many permutations from the code. The number one function for me from this list is converge .

Note: it may seem that the above problem only occurs with functions with more than 1 parameter, but add() does not have this problem, add(__, 3) and add(3) same.

+1
source

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


All Articles