Pipe atomic expressions#
Code Example
Runnable Example in Jac and JacLib
"""Pipe atomic expressions: Atomic forward pipe operator (:>) for chaining."""
with entry {
# Atomic forward pipe :> (higher precedence than |>)
"Hello" :> print;
# Chained atomic pipes
"Test" :> type :> print;
# Atomic pipe with lambdas for function composition
result = 5 :> (lambda x: int : x * 2) :> (lambda x: int : x + 10);
print(result);
}
Jac Grammar Snippet
Description
Pipe Atomic Expressions
The atomic forward pipe operator :> enables elegant data pipelines by passing values from left to right through a series of transformations.
Basic Atomic Pipe Syntax
Line 5 introduces the atomic forward pipe operator. This takes the string "Hello" on the left and pipes it to the print function on the right. It's equivalent to print("Hello"), but the pipe syntax emphasizes the flow of data from source to destination.
The :> operator reads naturally: "take this value and send it to this function."
Chaining Multiple Pipes
Line 8 demonstrates the real power of pipes - chaining transformations. This creates a pipeline:
graph LR
A["Test"] --> B[type function]
B --> C[<class 'str'>]
C --> D[print function]
D --> E[Output to console]
The execution flows left-to-right:
1. Start with the string "Test"
2. Pipe it to type(), which returns the type object <class 'str'>
3. Pipe that type object to print(), which displays it
Compare this to the nested function call equivalent: print(type("Test")). The piped version reads in the same direction as the data flows, making the sequence of operations clearer.
Pipes with Lambda Functions
Lines 11-12 show using pipes with lambda expressions for custom transformations. This creates a computation pipeline:
| Step | Operation | Input | Output |
|---|---|---|---|
| 1 | Start | - | 5 |
| 2 | First lambda: multiply by 2 | 5 | 10 |
| 3 | Second lambda: add 10 | 10 | 20 |
The value 5 is doubled to 10, then increased by 10 to get a final result of 20.
The lambda syntax (lambda x: int : x * 2) defines an anonymous function:
- lambda keyword starts the lambda
- x is the parameter name
- : int is the return type annotation
- x * 2 is the function body
Why Atomic Pipes?
The atomic pipe operator is called "atomic" because it passes the complete value as a single unit to each function. Unlike some pipe operators that might do partial application, :> always passes the entire left-hand value as the argument to the right-hand function.
Benefits of Pipe Syntax
Pipes make code more readable when you have multiple transformations:
Without pipes (nested):
With pipes (linear):
For longer chains, the difference is even more dramatic:
Without pipes:
With pipes:
The piped version reads like a recipe: "Take the value, apply func1, then func2, then func3."
Practical Applications
Atomic pipes are especially useful for: - Data transformation pipelines - Method chaining alternatives - Functional programming patterns - Making data flow explicit in your code
When you see :>, think "and then send it to" - it describes the journey of data through your program.