Video course contents16
LESSON 04 · HHY 1.7.0
Understand the Pipe Operator
Compare ordinary calls and pipelines while cleaning a short piece of text.
Your progress stays in this browser. No account needed.
Read the companion manual chapter →
Chapters & transcript
Select a timestamp to jump in the video. Expand a title to read the transcript and code.
Start with the desired result
The pipe operator lets us read a sequence of transformations in the order that data travels. Our example starts with text that has extra spaces and ends with a clean uppercase message. We will write the same work as ordinary calls, named intermediate values, and a pipeline. These forms help explain each other. The purpose is readable composition, so keep the intermediate types in mind instead of treating the pipe as punctuation that works everywhere.
let raw = " hello hhy "
raw |> trim |> upper |> print
# HELLO HHYFollow along with the downloadable example.
Ordinary calls first
Start with function calls you can already recognize. Trim receives raw text and returns cleaned text. Upper receives that result and returns uppercase text. Print displays the final value. Each binding makes one stage visible and gives you a convenient place to inspect a problem. This expanded form is a useful debugging tool even when the final script uses pipes. If a long expression is confusing, give its stages names before changing its logic.
let raw = " hello hhy "
let cleaned = trim(raw)
let loud = upper(cleaned)
print(loud)Follow along with the downloadable example.
Connect the same stages
Now place the starting value at the top and put one operation on each following line. The output from one stage becomes the input to the next. The result should match the ordinary calls exactly. This layout becomes especially helpful when a script describes a workflow. Read it as raw text, then trim, then uppercase, then print. The pipe changes how we write the composition; it does not remove the functions or their input requirements.
let raw = " hello hhy "
raw
|> trim
|> upper
|> printFollow along with the downloadable example.
Supply another argument
A pipe call can also include arguments. In this example the piped text fills the first argument of tag, and the explicit prefix supplies the next argument. Compare the equivalent ordinary call, tag with HHY first and Language second. Defining this tiny function makes the argument order visible. When using an unfamiliar built in function, read its signature instead of guessing which position a piped value occupies.
fn tag(value, prefix) {
return prefix + value
}
"HHY" |> tag("Language: ") |> print
# Language: HHYFollow along with the downloadable example.
Understand the changing value
At this point every transformation returns a string, so the chain has a straightforward shape. Capture the value before printing if you want to reuse it or inspect its type. Print is an output operation and returns null; it is not a string transformation to place casually in the middle of this chain. Track the returned value at each boundary. A readable pipeline depends on compatible stages, not only on attractive vertical formatting.
let loud = " hello hhy "
|> trim
|> upper
print(type(loud))
print(loud)Follow along with the downloadable example.
Keep a custom stage small
A named function works as a stage too. Announce receives the cleaned text and adds a prefix, while the earlier functions remain responsible for whitespace and case. Keeping those responsibilities small makes the chain easy to reorder when that is meaningful. Here moving announce before upper would also uppercase the prefix, producing a different result. Composition is ordered work. Before rearranging stages, consider whether each transformation changes what later stages receive.
fn announce(text) {
return "Ready: " + text
}
" hhy "
|> trim
|> upper
|> announce
|> printFollow along with the downloadable example.
A collection needs a stream stage
A pipe does not automatically make every value a stream in our tested one point seven point zero examples. For a list transformation, introduce stream explicitly before map. Map produces another stream, and collect consumes it into a list that we print. We will examine laziness in the next lesson. For now, observe the types: list to stream, transformed stream to list. The connecting operator cannot replace those conversions.
[1, 2, 3]
|> stream
|> map { number -> number * 2 }
|> collect
|> printFollow along with the downloadable example.
Diagnose a mismatched stage
The separate error example deliberately omits stream. On the tested runtime, map expects a stream and rejects this list input. The repair is to insert the explicit conversion, not to change the arithmetic inside the closure. When a pipeline fails, inspect the boundary mentioned in the diagnostic: what did the previous stage return, and what does this stage require? This approach scales much better than replacing several operations at once.
[1, 2, 3]
|> map { number -> number * 2 }
|> collect
# Fix: insert |> stream before map.Follow along with the downloadable example.
Practice equivalent forms
For practice, write the announcement pipeline and an equivalent version using ordinary calls. Run both and verify the same result. Then move the announcement step before uppercase and explain the changed prefix. Finally, inspect the list example and name the value type after each stage. Once you can explain those boundaries, you are ready for streams: pipelines whose elements are pulled lazily and whose consumption must be planned deliberately.
# Add announce after upper.
# Write the same work with ordinary calls.
# Run both versions and compare output.
# Try moving announce before upper.Follow along with the downloadable example.
