introduce

The functional has a number of utility methods for handling input parameters

identity

function identity(v) {
  return v
}
Copy the code

unary

function unary(fn) {
  return function onlyOneArg(arg) {
    return fn(arg)
  }
}
Copy the code

spreadArgs

function spreadArgs(fn) {
  return function spreadFn(argsArr){
    returnfn(... argsArr) } }Copy the code

gatherArgs

function gatherArgs(fn) {
  return function gatheredFn(. argsArr) {
    return fn(argsArr)
  }
}
Copy the code

reverseArgs

function reverseArgs(fn) {
  return function argsReversed(. args) {
    returnfn(... args.reverse()) } }Copy the code

. .

It’s all very simple. Indeed, some functions are so simple that you don’t even know what they can do with it. The functions in the function are like blocks, and each block looks so simple. How to “stack” the blocks, you can compose them, and we’ll see how that works later, okay

We’ll look at partial and curry methods that handle input parameters. They are native to JAVASCRIPT, but functions use them more as tools.

partial

For example, you have an Ajax function

function ajax(url, data, callback) {
  // ...  
}
Copy the code

You know the URL in advance, but data and callback may not be known until the user has finished typing the form. You can create a new function that calls Ajax internally, passes in the URL, and waits for the data and callback arguments

function getPerson(data,cb) {
  ajax( "http://some.api/person", data, cb );
}
Copy the code
function getOrder(data,cb) {
  ajax( "http://some.api/order", data, cb );
}
Copy the code

And pretty soon, if you do this manually, it gets pretty boring, especially if you know the parameter changes, like we know not only the URL but also the data in advance

function getCurrentUser(cb) {
  getPerson( { user: CURRENT_USER_ID }, cb );
}
Copy the code

In this case, we need to find a more general tool approach and observe carefully that we apply some of the arguments to the parameters in advance and defer the rest

Partial – This reduces the number of input arguments to a function

// sq:
function partial(fn, ... presetArgs) {
  return function partiallyApplied(. laterArgs) {
    returnfn(... presetArgs, ... laterArgs) } }Copy the code

use

const getPerson = partial( ajax, "http://some.api/person" )
const getOrder = partial( ajax, "http://some.api/order" )
// You can write partial(ajax, 'http://xxx' {user: CURRENT_USRE_ID})
// But the following one is more suitable
const getCurrentUser = partial( getPerson, { user: CURRENT_USER_ID } )

// Expand getCurrentUser for ease of understanding
var getCurrentUser = function outerPartiallyApplied(. outerLaterArgs){
  var getPerson = function innerPartiallyApplied(. innerLaterArgs){
    return ajax("http://some.api/person". innerLaterArgs); }return getPerson({ user: CURRENT_USER_ID }, ... outerLaterArgs) }Copy the code

Case 2

function add(x, y) {
  return x + y
}

/ / [11, 12, 13]
[1.2.3].map(function adder(val) {
  return add(val + 10)})// Use partial to fit the add function to the map callback
[1.2.3].map(partial(add, 10))
Copy the code

partialRight

What if the above functions we know in advance are data and callback, but we don’t know the URL yet?

Version one uses reverseArgs(reverse parameters) and partial

function partialRight(fn,... presetArgs) {
  returnreverseArgs( partial( reverseArgs( fn ), ... presetArgs.reverse() ) ) }/ / use
function add(a, b, c, d) {
  return a + b * 2 + c * 3 + d * 4
}
const add2 = partialRight(add, 30.40)
// 10 + 20 * 2 + 30 * 3 + 40 * 4 = 300
add2(10.20)


/ / understand partialRight
// reverseArgs(fn) returns a function that reverses the arguments when fn is called
let fn2 = function argsReversed(. args) {
  returnfn(... args.reverse()) }// partial(reverseArgs(fn), ... Return function of presetargs.reverse ()
// If p is called, fn(... laterArgs.reverse(), ... presetArgs)
// So you need to reverse the parameter laterArgs, fn(... lasterArgs, ... presetArgs)
let p = function partiallyApplied(. laterArgs) {
  returnfn2(... presetArgs.reverse(), ... laterArgs) }Copy the code

I suggest you type in the code, or write it on paper

Version two actually, version one can be an exercise to help you understand, it can actually be more straightforward

// sq:
function partialRight(fn, ... presetArgs) {
  return function partiallyApplied(. laterArgs) {
    returnfn(... laterArgs, ... presetArgs) } }Copy the code

curry

Curry is the decomposition of a function that takes multiple arguments into a continuous chain function, where each function takes one argument and returns another function that takes the next. (Loose Curry can take multiple arguments per function)

The previous example of Ajax, if using Curry

var personFetcher = curriedAjax( "http://some.api/person" )
var getCurrentUser = personFetcher( { user: CURRENT_USER_ID } )
getCurrentUser( function foundUser(user){ / *.. * /});Copy the code

Curry is similar to partial, but curry returns a function that accepts only the next argument

// sq:
function curry(fn, arity = fn.length) {
  return (function nextCurried(prevArgs) {
    return function curried(nextArg) {
      var args = [...prevArgs, nextArg]

      if (args.length >= arity) {
        returnfn(... args) }else {
        return nextCurried(args)
      }
    }
  })([])
}
Copy the code

Note: function default form, destructor, expansion operator form… Fn. length is incorrect, so the function should be passed the correct number of arguments

In the previous example 2, curry was used

function add(x, y) {
  return x + y
}

var adder = curry( add )

[1.2.3].map(adder(10))
Copy the code

Example 3

function sum(. nums) {
  var total = 0;
  for (let num of nums) {
    total += num;
  }
  return total;
}

/ / 15
sum(1.2.3.4.5)

var curriedSum = curry(sum, 5)
/ / 15
curriedSum(1) (2) (3) (4) (5)
Copy the code

It’s actually possible to accept only one argument using partial by manually calling partial continuously all the time, whereas Curry does it automatically.

function add(a, b, c) {
  return a + b + c
}

const partial1 = partial(add, 1)
const partial2 = partial(partial1, 2)
const partial3 = partial(partial2, 3)
/ / 6
partial3()
Copy the code

Passing too many arguments can sometimes be cumbersome, so Curry is also allowed to pass multiple arguments, as most libraries do

// sq:
function looseCurry(fn, arity = fn.length) {
  return (function nextCurried(prevArgs) {
    return function curried(. nextArgs) {
      var args = [...prevArgs, ...nextArgs];

      if (args.length >= arity) {
        returnfn(... args); }else {
        return nextCurried(args);
      }
    };
  })([]);
}
Copy the code

Of course, Curry also has curryRight, but I’ll write about it later

summary

The actual implementation of the library might be a little different, since it takes into account more general methods and higher levels of abstraction, but the code above does a good job of helping us understand partial and Curry, To get a glimpse of the functional iceberg, it’s worth mentioning that in version one of the partialRight, we use.reverse(), which is a variation method of an array

let arr = [1.2.3]
arr.reverse()
/ / [3, 2, 1)
console.log(arr)
Copy the code

And the mutation method leads to the function being “impure,” which leads to the idea of a pure function, which we’ll talk about a little bit later, but if you want to understand this idea of pure, it’s best to read the article

As for the code style, if you prefer the arrow functions of ES6, you can use them as you like, but of course there are many benefits to naming functions, such as readability and debugging

reference

Functional Programming in JavaScript

mostly-adequate-guide

Functional-Light-JS

awesome-fp-js