Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

noun

  • FP: Functional Programming.
  • OOP: Object Oriented Programming
  • AOP: Aspect Oriented Programming, Aspect Oriented Programming.

takeaway

“Any fool can write machine-executable code, but a good programmer can write code that a fool can understand.” – Martin Fowler

This famous quote describes the criteria of a “good programmer” with great feeling (I think the translation is better than the original). OOP, AOP, FP all ultimately serve this basic point. Therefore, this article will not copy the popular introduction of functional programming on the market, but all around the basic point to carry out a choice, with the minimum cost, bring the greatest benefit.

background

At present, there are a lot of discussions about FP in the industry, but it is not easy to find some useful information that can really guide practice and improve productivity. As said in the introduction, the author in line with effectively improve productivity for the purpose of studying some FP related information, and then integrated with the usual practice, after choice and flexibility, sorted out this document. It aims to make FP practical and profitable for daily development.

In addition, from the current popular three frameworks, have gradually “data”. React as an example, its abstract idea can be simplified as UI = fn(state). The DOM operation process is shielded by means of VDOM, DIFF, JSX, etc. The mapping between state (pure data) and UI is realized. The impact will not be expanded.

The vast majority of front-end development today is dealing with data (state), rather than directly dealing with the DOM as Jquery did before.

It changes the way we think about coding from the ground up. It’s important to appreciate this. Since it is a data processing, immutable data, then FP is useful, and this will be the focus of this article.

Finally, the author simply lists the judgment basis that FP can be established in the front end:

  • React IMmutable and the concepts of one-way data flow fit well with FP;
  • Redux is the most widely used React state management library, which provides a typical example of FP front-end practice.
  • The iteration of ES introduces the arrow function,Array.prototype.mapAnd other obvious FP characteristics of the grammar, let a person can not help but associate;
  • I led a “department front End Quality Control” project and did some research on the “testability” of code. High testability means “high cohesion and low coupling” and is the cornerstone of project maintainability. FP is the best way to improve the testability of your code. See How to Convince the Front End to Write a Single test.
  • FP looks cool and can be used with ZB.

The target

  1. Select the characteristics suitable for front-end practice in FP and summarize them into practical specifications to guide development;
  2. With FP as the breakthrough point, it leads to the thinking of programming thought level, so that the content is more enlightening;
  3. The goal is to write code that “fools can understand” and reduce maintenance costs.

conclusion

PS: The following contents are highlights and applicable to most projects. The corner Case is not included in the discussion.

The space is too long, first throw the conclusion, the thinking process see below. Specification is:

In addition to the framework’s template code, there are no classes in the project (see “Front-end Doesn’t Need Classes”). Only functions are allowed, and only the following two types of functions are allowed, with the following constraints:

Pure functions

  1. All functions except callbacks (including lifecycle hooks) and initializations;
  2. There must be input and output parameters, i.e(params: any) => any;
  3. No immutable operations are allowed on input arguments, especially reference type data. If it has to be modified, cloneDeep generates a new object and then returns the new object.
  4. It’s not allowed in the codethis,fetch,localStorage,window,DOMObvious need to access external keywords or global variables;
  5. specialTo return toPromiseObject function, all usedasync/awaitCall and put itAs aPure functions (such as Ajax requests, which are not strictly pure functions, but have been modified);

Side effect function

  1. Can only be a callback or initialization function;
  2. There are no outgoing arguments, input arguments can only be provided by the callback function itself, and no additional input arguments can be introduced, i.e(parmas? : any) => void;
  3. Internal code is mostly declarative, not imperative;

As you can see, the rules only use the concepts of pure functions and side effects, and that’s it. There are no concepts such as Pointfree, Container, Monad, functor, and so on. You should never even introduce these concepts, otherwise it will only lead to more difficult maintenance. Even so, if you follow all of these rules, your project code quality is pretty good.

The body of the

What kind of code can a fool understand

The author summarizes it as follows:

  1. No Surprise;
  2. From a macro view of the overall code, I can quickly understand what the whole project is doing and what each module is responsible for, just like the table of contents in a book.
  3. In the middle view, the code for each module looks like a hybrid robot. Each child robot internal system, self-sufficiency, power (input) can fight alone; At the same time, the input and output is clear, there is a clear interface can be combined to form the whole;
  4. At the micro level, each line of code is mainly declarative code, with clear and readable code specifications. Please refer to “Front-end Code Specifications that Lint can’t Solve” and “Vue3 Best Practices Coding Specifications”.

HMM… It’s a pretty silly description. It makes sense. In fact, we can think of writing a project as writing a book, from the table of contents to chapters to word by word, it is very much like writing code. We are writing a book that any fool can understand.

Declarative vs. Imperative

As mentioned above, declarative code is dominant, as opposed to imperative code, which is described below. Let’s look at an example from Functional Programming:

/ / imperative
var makes = [];
for (i = 0; i < cars.length; i++) {
  makes.push(cars[i].make);
}

/ / the declarative
var makes = cars.map(function(car){ return car.make; });
Copy the code

Obviously, imperative code that writes out every step, meaning every step requires a command, doesn’t make sense at first glance; The declarative form can quickly be seen as a mapping between makes and cars, without looking at the specific logic, you can also guess from the name. So in the sense of quickly reading and understanding code, we should use the declarative programming paradigm.

However, at the micro level, the granularity is down to the implementation of the function, and every line of code must be read. Even using the imperative programming paradigm doesn’t make much difference, and it’s actually a better choice when it comes to break, continue, and so on. This measure can be grasped by oneself, below the circumstance that assures macroscopical whole quality is controllable, vary somewhat in microcosmic level, even occasionally play a few “flower lives” also do not hurt greatly.

PS: This is a trade-off with the concept of functional programming, where there are no loops, instead there are recursive calls. I don’t think so, because loops are clearly easier to understand than recursions (especially tail-recursions) because our target audience is “dumb”.

The benefits of pure functions

This section is mainly from “Functional Programming is north – The Pursuit of” Pure “reasons”, the author’s original parts will be marked, not marked parts are all cited.

The cache can be

This is easier to understand in terms of pure functions:

A pure function is one in which the same input always returns the same output without any observable side effects.

The same input means the same output, so you can cache, use space for time, speed up.

Portability/self-documentation

A pure function is completely self-contained; everything it needs is readily available. Think about it… What are the benefits of this self-sufficiency?

First, the dependence of pure functions is clear, so it’s easier to observe and understand — there’s no sneaky trickery.

/ / not pure
var signUp = function(attrs) {
  var user = saveUser(attrs);
  welcomeUser(user);
};

var saveUser = function(attrs) {
    varuser = Db.save(attrs); . };var welcomeUser = function(user) { Email(user, ...) ; . };/ / pure
var signUp = function(Db, Email, attrs) {
  return function() {
    var user = saveUser(Db, attrs);
    welcomeUser(Email, user);
  };
};

var saveUser = function(Db, attrs) {... };var welcomeUser = function(Email, user) {... };Copy the code

This example shows that a pure function must be honest about its dependencies so that we know its purpose. Just looking at the signature of the purely functional version of signUp, which will use Db, Email, and attrs, gives us enough information at a minimum.

Second, our applications become more flexible by forcing dependencies to be “injected” or passed as parameters; Because the database or the mail client or whatever is parameterized. If you want to use another Db, you just pass it to the function. If you want to use this trusty function in a new application, just pass the new Db and Email, it’s very easy.

Finally, if TS is incorporated, the types of input and output parameters are further clarified, and the role of each pure function can be more easily inferred, making reading code a smoother experience. See “Functional Programming North – Chapter 7: Hindley-Milner Type Signatures” for specific examples.

testability

This is also easy to understand because of the nature of pure functions, which simply give the function an input and then assert the output. It is worth mentioning that the article “Pointing north” mentioned:

In fact, we’re finding that the functional programming community is pioneering new testing tools that help us automatically generate inputs and assert outputs. This is beyond the scope of this book, but I highly recommend you try Quickcheck, a testing tool tailored for functional environments.

If even unit tests can be automated, this is very attractive.

rationality

(original) A: For a certain reason, I think “Reasonable” is a good translation. For a certain reason, it seems to express this meaning better. For example, during debugging, you can replace the call result of a pure function with a “temporary constant” without any concern, or even delete some deterministic code to simplify the complexity and improve the efficiency of debugging.

Can be parallel

Finally, and crucially, we can run any pure function in parallel. Because pure functions do not need to access shared memory at all, and by definition, pure functions do not enter a race condition due to side effects.

Parallel code is easy to implement in server-side JS environments and in browsers that use Web workers because they use threads. However, due to the complexity of impure functions, the current mainstream view avoids such parallelism.

How to deal with side effects

There is a more detailed analysis of side effects in “Functional programming means North – Side effects may include”. It says:

The “effect” itself is not harmful; the key part of the “side effect” is the “side effect”. Just as the “water” in a pool of stagnant water is not itself the incubator for larvae, the “death” is the cause of the formation of swarms. Similarly, the “side” of side effects is a breeding ground for bugs.

I think so. However, side effects are impossible to avoid and may include, but are not limited to:

  • Changing a file system
  • Insert records into the database
  • Send an HTTP request
  • Variable data
  • Print/log
  • Get user input
  • DOM query
  • Accessing system Status

The treatment of side effects in pure FP is very complicated. Personally, I think it will greatly increase the use cost if completely copied. At least, it is difficult for “fools” to understand. Therefore, THE author has been thinking about how to define a range based on the characteristics of the front-end project, to “circle” the side effects. Finally, the following conclusions are drawn:

With the exception of initialization and callback functions (including lifecycle functions), all functions can be written as pure functions.

Callbacks make up the bulk of the front-end code, so what’s the point of FP? So another rule was added:

The side effect function disallows a return value, i.e. (params? : any) => void

This one may sound confusing at first. Think about it, the absence of a return value means that nothing depends on the result of the function, shielding the downstream. In fact, this can control the “side effects” within a certain range. Although it looks stupid, the cost is low, and at least there is no need to worry about any unexpected occurrence during debugging.

conclusion

After summarizing the above content and adding some details, I have drawn a conclusion and written it at the beginning of the article. To be honest, this document was not easy to write, and there are many thinking conclusions that cannot be verified. This means that these conclusions require a lot of brain work. And the end result is likely to be a laugh. In any case, putting your thoughts into writing is a way of forcing yourself to improve them, and that’s enough.

Finally, you are welcome to discuss and give your opinion, which is another powerful power for your rapid growth.

“Reading makes a full man,conference a ready man,and writing an exact man” — Francis Bacon Writing makes an exact man “– Francis Bacon

reference

  • Analysis of Functional Programming and the Front End
  • Mostly – Adequate – Guide Functional Programming is north
  • Factions of Programming
  • Where is the real use of FRONT-END DEVELOPMENT JS functional programming? In [beard big ha] answer