Functional Programming (4): The Power of Currying

Get into the habit of writing together! This is the 10th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the details of the event

In the previous article , the fundamentals and core of functional programming and the characteristics of pure functions were discussed. This post will explore the currying of functions.

What is currying

A curried function is a function that returns a function until all arguments have been passed.

How currying works

Suppose we have the addfunction

const add = (a, b) => a + b
复制代码

The simplest implementation of currying is to make functions return functions like:

const add = (a) => (b) => a + b
复制代码

The method of use is as follows:

const addOne = add(1) // addOne = (b) => 1 + b
复制代码

Another implementation, suppose, we have a  curryfunction that takes a function as an argument, and then curries it:

const add = curry((a, b) => a + b)
复制代码

As we can see, curryis a function that uses another function to delay arguments. We can call it like this:

const addOne = add(1) // addOne = (b) => 1 + b
复制代码

First, we create it by passing 1 as the first argument to the curried addfunction addOne. This produces another function that waits for the rest of the arguments to be passed, the add logic is not executed until all arguments are passed.

addOne(2) // 3
复制代码

Pass 2 (as b) to addOne; perform logic 1+2.

Quick summary:
currya function takes a function and makes its arguments lazy.

Why Curry

Currying will make our code:

  1. more clean
  2. Less repetitive parameter passing and less verbose code
  3. better combination
  4. better repeatability

Why currying can make our code better

Mainly, some functions take "config" data as input
If we have functions that accept "config" parameters, we'd better curry them, because these "config" parameters may be used over and over again. For example, let's say we have a  translatorfunction that accepts localeand text to translate text:

const translator = (locale, text) => {/*translation*/}
复制代码

The usage is as follows:

translator('fr', 'Hello')
translator('fr', 'Goodbye')
translator('fr', 'How are you?')
复制代码

每次我们调用translator时,我们都应该传递locale 和 text。这是多余且不干净的,在每次调用中都会传递locale
我们可以柯里化translator函数:

const translator = curry((locale, text) => {/*translation*/})
const inFrench = translator('fr') 
复制代码

现在,inFrenchfr作为语言环境传递给柯里化的translator函数,并等待提供text。我们可以这样使用它:

inFrench('Hello')
inFrench('Goodbye')
inFrench('How are you?')
复制代码

柯里化确实帮一个大忙,我们不需要每次都指定区域设置,因为柯里化,inFrench具有区域设置了。

在柯里化之后——在这个具体的例子中。代码变得:

  1. 更加干净了
  2. 不那么冗长,不那么多余

因为我们把“配置”和实际的“数据”分开了。这在许多情况下都非常方便。

在实际工作中

在实践中,我们有动态语言环境(每个用户都有不同的语言),可能是fr、en、de或其他。因此,我们最好将inFrench重命名为translatetranslate可以加载任何语言环境。 现在我们有了一个translator,它将区域设置作为“配置”,将text作为数据。由于translator是柯里化的,所以我们能够将“config”和“data”参数分开。

为什么要将“配置”和“数据”参数分开?

许多组件和函数需要使用一些功能,但不应该或不能知道“配置”部分的数据。这些组件或函数只有“数据”部分。因此,这些函数将能够在不需要了解“配置”部分的情况下使用函数。 因此,该组件或功能将更少地与系统耦合,这将使组件更具可组合性和可维护性。

我们什么时候使用柯里化

当我们知道函数中有“config”和“data”时,我们最好用柯里化。 柯里化可以让我们把它们分开。这是一个成熟系统设计的标志。因为代码质量的一大支柱是关注点分离。 即使一个函数需要所有参数才能正常运行,我们仍然更清楚何时传递参数以及在程序的哪一层上传递参数。

闭包和柯里化的关系

Closure: is a function returned by a "parent" function that has access to the internal state of the parent function.
Currying: always leads to closures. Because every function returned by a curried function provides the internal state of the parent function.

more examples

Now we can look at some meaningful examples...
Example 1️:
Given a list of numbers, increment all numbers by 1
Input: [1,2,3,4,5]
Output: [2,3,4,5,6]
solution:

// the curried `add` function that we defined earlier
const addOne = add(1)
const incrementNumbers = map(addOne)
const incrementedNumbers = incrementNumbers(numbers)
复制代码

Example 2️:
Given a string, keep all words starting with the letter "C"
Input: "currying is awesome"
Output: "currying"
Solution:

const startsWithC = startsWith('c')
const filterStartsWithC = filter(startsWithC)
const filteredWords = filterStartsWithC(words)
复制代码

Remember, () => () =>. . . is another implementation of currying, a simpler version of currying.

in conclusion

Currying just makes parameters inert. If a function keeps returning a function until all its arguments are satisfied, then it executes logic. We also saw with practical examples how it can make our code cleaner, cleaner, more composable, and even more reusable. This takes advantage of the separation of concerns principle.

Translated from: blog.bitsrc.io/functional-…

Guess you like

Origin juejin.im/post/7084802365508812831