Skip to content
ZOLO / TE132 Type · error

`_` is only valid as a pipe slot or rooted access in a short-lambda argument

The `_` placeholder is only valid as a direct argument in the RHS call of a `|>`/`?>` pipe stage (at most one per stage), or as the head of a field/method/index chain used as a call argument (`_.field`). A stray `_`, a second `_` in the same pipe stage, or `_` as a pipe's left-hand side are all rejected.

Why this fires

The _ placeholder stands in for "the value being piped" or "the argument this short lambda receives". It only has a defined meaning in two positions:

  • As a direct argument in the RHS call of a pipe stage (|>/?>): x |> clamp(0, _, 100) splices the piped value into that slot and the pipe disappears — it becomes clamp(0, x, 100).
  • In a proved unary callback argument: xs.map(_ * 2), xs.map(_) and users.map(format_name(_.name)) become one-parameter lambdas. Every _ in the expression denotes the same parameter; nested callback slots bind independently. Existing access-rooted shorthand also remains available for untyped callback calls.

Everywhere else a bare _ is not a value — there's nothing for it to stand for — so the compiler rejects it as TE132. Three shapes trigger it:

1. Two or more _ in the same pipe stage

Only one _ per stage can be resolved to "the piped value" — a second one has nothing left to bind to.

fn f(a: int, b: int) -> int { return a }
let x = 5
let y = x |> f(_, _)
//           ^^^^^^^ error[TE132]: at most one `_` per pipe stage

2. A stray _ outside a pipe/short-lambda position

let x = 1 + _
//          ^ error[TE132]: `_` is only valid in the RHS of `|>`/`?>` or in a unary callback argument

fn f(a: int) -> int { return a }
let z = f(_)
//         ^ error[TE132]: `_` is only valid in the RHS of `|>`/`?>` or in a unary callback argument

A bare _ or scalar expression requires an actual unary callable parameter. Thus f(_) is invalid above because f expects an integer, while xs.map(_ + 1) is valid. An unresolved dynamic call does not establish the callback contract; name the parameter in an explicit lambda instead.

Inside a unary callback, ordinary nested value calls use its parameter: users.filter(check(_.age)) passes an age to check. A nested callback slot binds its own _. Explicit lambdas, blocks, collections, control flow and pipes also delimit scopes.

3. _ as the left-hand side of a pipe

fn f(a: int) -> int { return a }
let y = _ |> f(_)
//      ^ error[TE132]: `_` cannot be the left-hand side of a pipe

_ is only ever an RHS-argument placeholder or a short-lambda chain head — it can never be the value flowing into a pipe.

Fix it

1. Use at most one _ per stage

fn f(a: int, b: int) -> int { return a }
let x = 5
let y = x |> f(_, 2)      // ok — one placeholder

If the piped value is needed in more than one argument position, name it first:

let tmp = x
let y = f(tmp, tmp)

2. Use _ in a callback position or a pipe stage

let ys = xs.map(_.name)   // ok — `_` is the chain head, `.name` is the access
let adults = xs.filter(_.active && _.age >= 18) // one shared parameter
let doubled = [1, 2].map(_ * 2) // a proved unary callback
let y = x |> f(_, 100)    // ok — `_` fills the RHS call's placeholder slot

A bare _ outside those positions has no defined meaning; bind it to a name instead:

fn f(a: int) -> int { return a }
let x = 5
let z = f(x)               // fixed

3. Don't pipe from a bare _

fn f(a: int) -> int { return a }
let x = 5
let y = x |> f(_)          // ok — `_` is the RHS's placeholder, not the LHS

See also

  • /docs/operators — pipe (|>), try-pipe (?>), and placeholder syntax.
  • TE131 — named-argument call validation, the other TE13x front-end desugar.

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

enespt-br