A few mistakes I made in functional programming

[CSDN editor’s note] When it comes to programming ideas, do you first think of object-oriented or function-oriented programming? The author of this article shared some of the pitfalls he stepped on in the practice of functional programming, and shared them with everyone, hoping to help you.

Original link: https://ift.tt/x0faBys

Unauthorized reprinting is prohibited!

Author | Robert Pearce Translator |

Produced | CSDN (ID: CSDNnews)

In the past few years, when I used functional programming to develop applications with a lot of JavaScript code, I made many mistakes and took some detours. I will review and share with you.

Does not use static type checking

Do not use tools such as:

  • TypeScript

  • flow

  • ReasonML

  • Elm

 const processData = composeP(syncWithBackend, cleansePII, validateData)
// * What arguments and their types are expected here? // // * If each function is written like this, how can // one suss out what data are flowing where? // // * How hard is this going to be to debug? // Use this everywhere: `(x) => (console.log(x), x)`

What should be the types of these parameters? If every function is written like this, who can guess what data they pass? Debugging this code can be very difficult.

You might think that this “point-free or die” style of programming is the real problem. Then look at these two ways of writing:

 async function processData(data) { await validateData(data) const cleansedData = cleansePII(data) await syncWithBackend(cleansedData) return data }

Or use Promise chains:

 // or for the Promise-chainers…
const processData = data => validateData(data) .then(cleansePII) .then(syncWithBackend) .then(() => data)

Either way, think about who will understand what they do in three months.

Not using well-known code documentation tools

Such as jsdoc. These tools can make it difficult for other team members to understand the code, while also preventing the use of auto-completion.

Here is an example, note that the documentation tags are not standard jsdoc tags:

 // NOTE: this is an untested, small example
/** * @typedef {Object} ReportingInfo * @property {("light"|"dark")} userTheme - Current user's preferred theme * @property {string} userName - Current user's name * @property {UUID} postId - The current post's ID */
/** * Validates that the reporting data (current user site prefences and post info) * is OK, removes personally identifiable information, syncs this info with the * backend, and gives us back the original data. * * @param {ReportingInfo} data - The current user's site preferences and post info * @returns {Promise<ReportingInfo>} - The original reporting data */ const processData = data => // …

Not seriously training old and new colleagues

Don’t think that writing a few articles, finding some learning resources, giving them to people who are new to functional programming, and encouraging them to ask questions will lead to good results.

And don’t go to the other extreme: spending all your time and energy on a few people, ignoring others, not recording learning experiences, and not encouraging them to help each other.

No need to bother other engineering teams to join, and no need for everyone to work in the same direction

“I build it and they notice… don’t they?”

Learning functional programming during your lunch break? No, what if they find that I don’t understand anything?

Meet other team leads, ask them if they are interested in functional programming, see what else they could do better, or hear why they are not interested? No, this matter has to be reported to the manager, they will think that I am too naive or too troublesome, so the loss outweighs the gain?

By keeping what you have learned privately, other teams cannot contribute and cannot improve the status quo.

Better use the wrong abstraction than the wrong repetition

At RailsConf 2014 in Chicago, USA, I was impressed by Sandi Metz’s talk (https://www.youtube.com/watch?v=8bZh5LMaSmE&ab_channel=Confreaks), where she said that “even if you have to Repeat, and don’t use the wrong abstraction”. Two years later, she blogged about “The Wrong Abstraction” with some great reviews (https://ift.tt/AcVatvd).

She believes that extracting core business logic and repeatedly abstracting it into broad concepts will eventually lead to no one fully understanding these concepts, let alone how these abstractions work.

If no one can understand these abstractions, the review of PR will become a simple like, and people will be lost soon.

Not refactoring legacy code that doesn’t fit the team

The only purpose of the old code is to tell the members who just joined the team how bad your code was back then. This code should have been rewritten long ago, but many people put their time and energy into writing new functions.

Use compose to connect all API calls, making debugging more difficult

This code looks fine:

 // handler for POST /posts
import { createPost } from 'app/db/posts' import { authenticateUser, authorizeUser } from 'app/lib/auth' import { trackEvent } from 'app/lib/tracking'
const validateRequestSchema = payload => { /* … */ }
export const handleCreatePost = curry(metadata => pipeP( authenticateUser(metadata), authorizeUser(metadata), validateRequestSchema, createPost(metadata), tapP(trackEvent('post:create', metadata)), pick([ 'id', 'authorId', 'title' ]) ) )

But can you spot that this code takes two parameters? Did you know that authenticateUser ignores the second parameter? how do you know? What about trackEvents? Does it accept payloads? Or createPost() will return data related to the post?

It is much better to change it to this:

 export async function handleCreatePost(metadata, payload) { await authenticateUser(metadata) await authorizeUser(metadata, payload) await validateRequestSchema(payload)
const post = await createPost(metadata, payload)
await trackEvent('post:create', metadata, payload)
return { id: post.id, authorId: post.authorId, title: post.title, } }

I am not saying that the second way of writing is better than the first way of writing, but the first way of writing does make people scratch their heads.

Reinvent procedural programming and call it “declarative”

 const setBookReadPercentByType = (contentType, statusObject) => assoc( 'readPercent', pipe( prop('subItems'), values, filter(propEq(contentType, 'chapter')), length, flip(divide)(compose(length, keys, prop('subItems'))(statusObject)), multiply(100), Math.round )(statusObject), statusObject )

Patterns using a large number of function compositions

For example, the following are the four function combination modes, each of which can also be written as a Promise version, plus various combinations between them, not mentioning the use of pipeWith, composeWith, etc.

 // ? These 4, plus Promisified versions of them, // plus combinations of them all used at once; // doesn't include ramda's pipeWith and composeWith
// compose const getHighScorers = compose( mapProp('name'), takeN(3), descBy('score') )
// pipe const getHighScorers = pipe( descBy('score'), takeN(3), mapProp('name') )
// composeWithValue const getHighScorers = players => composeWithValue( mapProp('name'), takeN(3), descBy('score'), players )
// pipeWithValue const getHighScorers = players => pipeWithValue( players, descBy('score'), takeN(3), mapProp('name') )
// …but then now mix and match them with actual, // real-life business logic.

The code is full of obscure algebraic operators

Using these algebraic operators in your code is sure to confuse your teammates:

  • Task, Maybe, Either, Result, Pair, State

  • bimap

  • the chain

  • bichain

  • option

  • coalesce

  • fork

  • sequence

  • ap

  • map — I don’t mean Array.prototype.map, nor new Map(), nor the map of key-value objects

Transform the data yourself instead of letting SQL do it

Originally, SQL is very suitable for converting data, but it is necessary to build a data pipeline under the name of “immutable data”, and convert it step by step in the pipeline, so as to consume as much memory as possible in this way.

Suggest following your functional style in a colleague’s PR

“This code is very nice, what if you reverse all the parameters, delete the intermediate variables, and map these operations to Either?”

or

“I noticed that you explicitly constructed these objects in the function. If you could use <some function>, you could define the shape of the output object, and then use the function as a value to look up or compute each value.”

There’s some left…

  • Share some functional programming articles that beginners can’t understand, making them feel bad;

  • Continue to write code in functional style, although no second person in the whole team does so;

  • Negatively use emojis to comment on other people’s PRs;

  • Give a presentation on “Functional Programming” at the company and broadcast your mistakes to the entire company.

Summarize

Many of the mistakes mentioned above are ostensibly the result of inexperience and lack of technical leadership. But I think the real reason should be deeper.

Finally, we cannot discard the core principles of functional programming:

  • Immutability: recreate the object instead of modifying the original object for better performance;

  • Pure functions: call the same function with the same parameters and get the same result;

  • put side effects on the logical edge of the program;

  • Very few classes, no inheritance, no map/filter/reduce etc.

The text and pictures in this article are from CSDN

loading.gif

This article is transferred from https://www.techug.com/post/several-mistakes-i-made-in-functional-programmingb4e163e36c8b3265ca53/
This site is only for collection, and the copyright belongs to the original author.