Check out my personal blog teobler.com for the latest articles
From generic to special
In a previous article, we mentioned the importance of the “shape” of a function in functional programming. We strive for every function to be unary so that we can combine functions together, but we always encounter various situations that force our functions to be unary, binary, or even multivariate. This is when we need to “change the shape” of the function to fit other functions with the help of currization. It is also the process of moving a function from “generalization” to “specialization,” which is extremely important in functional programming.
const ajax = (url, data, cb) {/ *.. * /}
ajax(CUSTOMER_API, { id: 33 }, renderCustomer);
Copy the code
Let’s say we have a function called Ajax, and we call it with some parameters, and what it does is it renders the UI in the page by asking some API to get the data. A very common functions, but perhaps we at the time of call for too much information, this need to code the reader to think about what happened here, we can make it more simple, the message can let the reader an instant clear understanding to express the content of the code, of course is not merely in order to make the code more short, actually may take longer.
const getCustomer (data, cb) {
return ajax(CUSTOMER_API, data, cb);
}
getCustomer({ id: 33 }, renderCustomer);
Copy the code
Because URL is actually a hard code, we only need request data and callback to call this function, so we can wrap Ajax with getCustomer, and then call it again, one variable will be missing and some information will be lost. It lets the reader see at a glance what the function is trying to do. But the credit comes from the function name, which is more semantic and purposeful than Ajax.
Such changes actually did not make the code simple, but because many layer encapsulates the function, seemingly code becomes complicated, but you can be a function of the “general” into a “specialization” function, and give it took a semantic function, can let a person see what is the special function of purpose, easier to understand.
Following this line of thinking, we can actually take this one step further and “specialize” it to convey deeper information to the reader. For example, if we need to retrieve the information of a specific logged-in user, we could have the following function to express this information in more detail:
const getCurrentUser (cb) {
// return ajax(CUSTOMER_API, { id: 33}, cb);
return getCustomer({ id: 33}, cb);
}
getCurrentUser(renderCustomer);
Copy the code
It is worth noting that when we define getCurrentUser, we can actually use ajax to define it, not because getCustomer allows usto express the hierarchical relationship between functions more clearly.
Parameters of the order
When you want to move a function from “generalization” to “specialization” — that is, from a function of several variables to a function of one or two variables — we need to focus on the order in which arguments are passed. The priority of the parameters should also be from general to specialized, that is, the most easily passed parameter should be a general parameter, followed by more and more specific parameters. If you start with a very specific argument, subsequent arguments will be difficult to match, and the function will become a single function.
For example, in the Ajax function above, if THE initial argument I pass in is a callback, then the new Currization function I get can only be used as render and nothing else, which also violates the principle of reusing function components in functions. Therefore, when designing functions, the order of parameters is a very important point, which can be learned from the tripartite libraries of various functions (ramda.js, Lodash/FP, etc.).
The map function in the Ramda library, for example, takes a callback as its first argument and passes in the callbak array as its second argument. The first argument makes the map function a specialized Mapper that can then be easily reused, accepting various inputs and returning desired values. If the first argument is an array, the new function returned will be a map function for that array only.
Partial and Curried functions
Partial functions and currification are two utility functions that every functional programming library provides. With these two functions, we don’t have to write “function specialization” by hand. We don’t care how they’re implemented internally, we’ll just talk about how they’re used.
The name partial function is a bit familiar. Yes, it comes from partial derivatives in mathematics:
In mathematics, a partial derivative of a multivariable function is its derivative with respect to one of the variables while keeping the other variables constant (in which all variables are allowed to change with respect to the total derivative).
Partial functions are equivalent to setting some of the parameters needed by a function in advance and returning a new function to wait for the next parameter to come in:
const ajax(url, data, cb) { / *... * / }
const getCustomer = partial(ajax, CUSTOMER_API);
// const getCurrentUser = partial(ajax, CUSTOMER_API, { id:33 });
const getCurrentUser = partial(getCustomer, { id: 33 });
getCustomer({ id: 33}, renderCustomer);
getCurrentUser(renderCustomer);
Copy the code
Familiar with functional classmates could be more like curry, curry is changed to a accept multiple parameters function into a only accepts a parameter function, didn’t receive a new parameter, will return a new function to wait for the next one parameter, until after completion of all the parameters to accept returns the result of the operation:
// const ajax => url => data => cb => ajax(url, data, cb);
function ajax(url) {
return function getData(data) {
return function getCB(cb) {
return ajax(url, data, cb);
}
}
}
ajax(CUSTOMER_API)({ id: 33})(renderCustomer);
const getCustomer = ajax(CUSTOMER_API);
const getCurrentUser = getCustomer({ id: 33});
Copy the code
Of course, here we define a natural Corrified function by hand, but it would be too tiring to write every function like this, so we use the library to do it for us:
const ajax = curry(3.function ajax(url, data, cb) {/ *... * /});
const getCustomer = ajax(CUSTOMER_API);
const getCurrentUser = getCustomer({ id: 33});
Copy the code
The Curry function asks you to tell it how many arguments you want the function to take, and then pass in that function.
Similarities and differences
- Both serve the same purpose – to turn a general function into a specialized function.
- You only need to specialization your function once, and then the function can be specialized at any level. And there are no pre-passed arguments. All the arguments need to be passed in for subsequent use.
- Partial functions are “once typed”. Each time you want to continue to change the shape of the function, you need to call the utility function again, passing in some of the initial arguments at the beginning, and passing in the rest once.