Pipeline Operator – Cleaner Function Chains
In JavaScript development, you often encounter long chains of function calls, especially when processing data: you call a function, pass the result to the next function, format the data further, filter it, transform it again – and so on. With the Pipeline Operator (|>), which is still in development, this process should become much more readable and clearer.
Important Note: The Pipeline Operator is currently still a TC39 proposal and not yet officially standardized in JavaScript. To try it out, you need a transpiler like Babel with the appropriate plugin. Nevertheless, it is worth taking an early look at this concept to learn about the future of function composition.
What is the Pipeline Operator?
The Pipeline Operator |> is inspired by similar concepts in other languages like Elixir or F#. It allows representing function calls in a declarative, pipeline-like manner. Instead of having to read nested function calls from inside to outside, you can follow the data flow from left to right.
Without Pipeline Operator:
const result = formatData(capitalize(trim(inputData)));
Here you have to read from inside to outside to understand what happens: first trim is applied to inputData, then capitalize on the result, and finally formatData.
With Pipeline Operator:
const result = inputData
|> trim
|> capitalize
|> formatData;
Now the data flow is clearly readable from top to bottom, or from left to right. inputData goes into trim, the result of trim into capitalize, and so on. This significantly improves the readability of complex function chains.
How Does the Pipeline Operator Work?
The Pipeline Operator takes the value on the left side and passes it as an argument to the function call on the right side. Technically, the syntax is:
value |> function
This is equivalent to:
function(value)
Extended Example
Traditional approach:
function processUserInput(input) {
const trimmed = trim(input);
const normalized = normalize(trimmed);
const validated = validate(normalized);
const transformed = transform(validated);
return format(transformed);
}
With Pipeline Operator:
function processUserInput(input) {
return input
|> trim
|> normalize
|> validate
|> transform
|> format;
}
With Arrow Functions
When functions need additional arguments, you can use arrow functions:
const result = data
|> (x => filter(x, predicate))
|> (x => map(x, transformer))
|> (x => reduce(x, reducer, initial));
Or with a more concise syntax (if supported):
const result = data
|> filter(%, predicate)
|> map(%, transformer)
|> reduce(%, reducer, initial);
(Note: The % placeholder syntax is part of the “Hack” style proposal)
Practical Examples
1. Data Processing Pipeline:
const processOrders = orders
|> (x => x.filter(order => order.status === 'pending'))
|> (x => x.map(order => ({ ...order, total: calculateTotal(order) })))
|> (x => x.sort((a, b) => b.total - a.total))
|> (x => x.slice(0, 10));
2. String Manipulation:
const cleanText = input
|> trim
|> toLowerCase
|> removePunctuation
|> normalizeWhitespace;
3. Async Operations:
const userData = await userId
|> fetchUser
|> (user => fetchUserDetails(user.id))
|> enrichUserData
|> cacheUser;
4. Mathematical Operations:
const result = value
|> Math.abs
|> Math.sqrt
|> Math.floor
|> (x => x * 2)
|> (x => x + 10);
Benefits
- Better Readability: Data flow is immediately obvious
- Reduced Nesting: No deeply nested function calls
- Easier Debugging: Each step can be isolated
- Better Composition: Functions can be easily combined
- Left-to-Right Reading: Natural reading direction
Comparison with Alternatives
Method Chaining:
// Works, but limited to methods
array
.filter(x => x > 0)
.map(x => x * 2)
.reduce((sum, x) => sum + x, 0)
Composition Functions:
// Works, but reads right-to-left
const process = compose(
format,
transform,
validate,
normalize,
trim
);
Pipeline Operator:
// Reads naturally left-to-right
const result = input
|> trim
|> normalize
|> validate
|> transform
|> format;
Current Status and Adoption
- Stage 2 Proposal in TC39
- Not yet in browsers natively
- Available with Babel plugin
- Similar features in TypeScript and F#
- Active development and discussion
How to Use Today
With Babel and the appropriate plugin:
npm install --save-dev @babel/plugin-proposal-pipeline-operator
.babelrc:
{
"plugins": [
["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }]
]
}
Conclusion
The Pipeline Operator promises to make JavaScript code more readable and maintainable. While it’s not yet standardized, the concept is powerful and worth understanding. As it progresses through the TC39 process, it may become a standard feature of JavaScript, revolutionizing how we write function compositions.
Keep an eye on the Pipeline Operator proposal and be ready to adopt it when it becomes available!