Skip to content

Functional Chains

The backtick-dot operator transitions from a legacy command (byte-stream) to a functional range (line-stream).

functional_expr ::= '`' command '`' '.' method_chain
method_chain ::= method_call ('.' method_call)*
method_call ::= method_name ['(' args ')']

A legacy command must be wrapped in backticks to enter functional mode. The dot immediately after the closing backtick begins the chain. Everything inside backticks is parsed as a standard shell command (with pipes, redirects, arguments).

`ls -la`.sort()
`grep -r "error" src/`.filter(x => x.length > 0).take(10)
`cat file.txt`.sort().map(x => x.toUpper())

POSIX pipes inside backticks work normally:

`cat log.txt | head -100`.filter(x => x.contains("WARN"))

A functional chain’s output can feed into a POSIX pipe:

`ls -la`.filter(x => x.contains(".d")).sort() | wc -l

A POSIX pipe can feed into a backtick expression:

cat server.log | `grep "ERROR"`.sort().unique()

Parentheses are optional when a method takes no arguments:

`ls`.sort # equivalent to `ls`.sort()
`ls`.sort.take(5) # mix freely

When a method takes arguments, parentheses are required.

By default, backtick expressions capture stdout. Named stream methods select which stream to use:

MethodDescription
.stdoutSelect stdout only (default, implicit)
.stderrSelect stderr only
.mergeCombine stdout and stderr into one stream
.captureRun once, return object with stdout, stderr, and exitCode
`make`.stderr > errors.txt
`make`.merge.filter(x => x.contains("error"))
let result = `make`.capture
result["exitCode"]
result["stdout"]
result["stderr"]

Standard POSIX redirections work with backtick expressions:

`ls -la` > files.txt # stdout to file
`ls -la` >> log.txt # stdout append
`cat < input.txt`.sort # stdin from file

Methods like map and filter accept lambdas:

# explicit single-param
`ls`.filter(x => x.endsWith(".log"))
# multi-param (for reduce)
[1, 2, 3, 4].reduce(0, (acc, x) => acc + x) # 10
# implicit lambda -- the parameter is always `a`
[1, 2, 3].map(a + 10) # [11, 12, 13]
[1, 2, 3, 4].filter(a > 2) # [3, 4]
`ls`.map("-> ${a}") # string interpolation in implicit lambda

When a method receives a non-lambda expression, it wraps it as a => <expr>. The implicit a shadows any outer variable with the same name.