Skip to content

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);
}
"""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);
}
1
2
3
4
5
6
7
"""Atomic expressions: Atomic forward pipe operator (:>) for chaining."""
from __future__ import annotations
from jaclang.runtimelib.builtin import *
print('Hello')
print(type('Test'))
result = (lambda x: x + 10)((lambda x: x * 2)(5))
print(result)
Jac Grammar Snippet
atomic_pipe: (atomic_pipe A_PIPE_FWD)? atomic_pipe_back

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.