---
title: Lambdas & Reducers
description: >-
  Arrow-function lambdas, closures, and the reducer methods (map, filter,
  reduce, forEach, find, some, every) that replace most explicit loops.
---

<div class="flex gap-3 mb-6">
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-blue-50 text-blue-600 text-sm font-medium">
    Intermediate
  </span>
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-gray-100 text-gray-600 text-sm font-medium">
    10 min read
  </span>
</div>

## Introduction

kScript v3 adds JavaScript-style **arrow lambdas** and a family of **reducer methods** on arrays. Together they replace most explicit `for` loops with one-line transformations, and they are the recommended way to iterate: every reducer pass is automatically protected by the runtime's iteration watchdogs, and the analyzer type-checks the callback's element types.

Pine Script has no lambdas at all. A transformation that takes a Pine `for` loop and manual accumulator state is typically a single expression in kScript: that conciseness is measured, not aspirational (the engine's own test suite asserts a representative transformation at half the Pine line count).

## Lambda syntax

```javascript
var double = (x) => x * 2
var label = (p, side) => {
  var prefix = side > 0 ? "bid" : "ask"
  return format("{0} @ {1}", prefix, tostring(p))
}

var y = double(21)          // 42
```

Two forms:
- **Expression body**: `(x) => x * 2`, the expression is the return value.
- **Block body**: `(x) => { ... return v }`, with explicit `return`.

Lambdas are values. Store them in `var` variables, pass them to functions and reducers, return them from functions, and call them directly. They **close over** the surrounding scope:

```javascript
var threshold = high * 0.99
var nearHigh = prices.filter((p) => p > threshold)
```

{% hint style="warning" %}
**Lambdas cannot be stored in `persist`/`static` state.** Carried state must survive the engine's snapshot-and-rollback machinery, and closures cannot be safely snapshotted. The compiler rejects `persist f = (x) => x` at the declaration with a clear error. Regular `var` lambdas are recreated each bar and are always safe.

{% endhint %}
## The reducer methods

All seven take a lambda and iterate a **snapshot** of the array (mutating the array inside the callback does not affect the active pass):

| Method | Callback | Returns |
|---|---|---|
| `map(fn)` | `(value, index?) => newValue` | New array of transformed values |
| `filter(fn)` | `(value, index?) => boolean` | New array of passing values |
| `reduce(fn, initial)` | `(accumulator, value, index?) => accumulator` | The final accumulator |
| `forEach(fn)` | `(value, index?) => void` | Nothing (side effects) |
| `find(fn)` | `(value, index?) => boolean` | First match, or `na` |
| `some(fn)` | `(value, index?) => boolean` | `true` if any match |
| `every(fn)` | `(value, index?) => boolean` | `true` if all match |

```javascript
var sizes = book.map((lvl) => lvl[1])
var bigLevels = book.filter((lvl) => lvl[1] > 50)
var totalSize = sizes.reduce((sum, s) => sum + s, 0)
var hasWall = book.some((lvl) => lvl[1] > 500)
```
### Callback arity is lenient

A callback may declare fewer or more parameters than the method supplies; parameters the method does not provide are bound to `na`. Direct lambda calls (not through a reducer) keep strict arity checking. This mirrors how every mainstream language treats callbacks and keeps one-parameter lambdas clean.

## The microstructure idiom

Reducers are how kScript turns raw order-flow rows into indicator values. The canonical pattern, from cells to metric to plot:

```javascript
timeseries fp = source("footprint", symbol=currentSymbol)

// net delta for the current bar
timeseries delta = fp.cells.map((c) => c[2] - c[3]).reduce((s, x) => s + x, 0)

// price of the largest-volume bucket
var top = fp.cells.reduce((best, c) =>
  (c[2] + c[3]) > (best[2] + best[3]) ? c : best, fp.cells[0])
plotLine(top[0], color="#f59e0b")
```



Every windowed indicator in the [TA library](../functions/ta-library.md) also accepts microstructure-derived series, so `rsi(source=delta, period=14)` is a delta-RSI in one line. None of this is expressible in Pine Script: it has neither the data nor the lambdas.
## Loops still exist, and they are guarded

`for`, `for...in`, `for...of`, and `while` remain available for logic that does not fit a reducer. Every loop body and every reducer pass is protected by the same watchdogs: a per-loop iteration ceiling, a per-run aggregate ceiling, and mid-bar interruption support, all reporting precise line/column errors. A runaway loop terminates loudly; it cannot hang a chart. The numbers live in [Limitations](../faq/limitations.md).

**Rule of thumb:** reach for a reducer first, `for...in`/`for...of` second, and a manual indexed loop only when you genuinely need index arithmetic.
