Recipes, not loops!

If you're accustomed to writing loops to transform data, stop! Deephaven works fundamentally differently, and understanding this difference early saves you frustration and helps you write better, faster code.

The fundamental paradigm shift: recipes, not loops

How you might think

In traditional programming, you tell the computer exactly how to process each element:

This loop processes one element at a time. You're giving step-by-step instructions for how to process the data.

How to think in Deephaven

In Deephaven, you specify what you want, not how to compute it. You write a recipe that describes the transformation, and the engine figures out the optimal way to execute it. Table operations such as update take recipes as query strings:

There's no loop. You describe the relationship (XSquared = X * X), and the engine applies it to every row. This works the same way for arbitrarily complex operations.

When you write XSquared = X * X, you're not saying:

  • "Start at row 0"
  • "Read X from row 0"
  • "Multiply X by itself"
  • "Store the result in XSquared at row 0"
  • "Go to row 1"
  • "Repeat..."

You're saying:

  • "For every row, XSquared should equal X times X"

The engine decides:

  • How to split the data into batches of rows for fast processing.
  • Whether to parallelize the operation.
  • Which rows to recompute when data changes.

Why this matters

For static data

Even for static, one-time calculations, the recipe approach has advantages:

  1. Clearer code — declarative recipes are easier to read than imperative loops.
  2. Faster execution — the engine evaluates formulas over batches of rows in compiled code.
  3. Less error-prone — no manual loop management or index tracking.

For real-time data

This is the critical difference. Loops execute once and stop. Recipes keep applying to new rows as they arrive.

Watch what happens:

  • The table keeps updating — new rows appear about every second.
  • You wrote the recipe once, and the engine applies it to every new row.

Compare a loop that uses a column iterator to read values one at a time:

The engine is smart about updates

When the source data changes, the engine:

  1. Tracks dependencies — it knows that XSquared depends on X.
  2. Computes incrementally — each update tells the engine which rows were added, which existing rows changed (modified rows), and which columns changed. The engine computes XSquared for the added rows. For modified rows, it recomputes XSquared only when X is one of the changed columns.
  3. Updates results in place — it writes the new values into the result table, so you never re-run the query.

With loops, this requires significant additional infrastructure. A loop executes once and stops, so you'd have to write your own code to detect new and changed rows and recompute them. With recipes, it's automatic.

Common patterns: wrong vs. right

You've already seen the simplest pattern, creating a column from another column, in How to think in Deephaven. Two other common loops have recipe equivalents.

Pattern: conditional logic

❌ Wrong (loop approach):

✅ Right (recipe with the ternary-if operator). The operator has the form condition ? valueIfTrue : valueIfFalse:

Pattern: running calculations

❌ Wrong (loop with accumulator):

✅ Right (use updateBy):

When loops are appropriate

Recipes replace loops for transforming data, but loops are still the right tool for extracting data from a table and for control flow in your own code.

✅ Extracting data from Deephaven

To read two columns side by side, open one iterator per column and advance them together, closing both when you're done:

See the guide to extracting table values for details.

✅ Control flow in your Groovy code

A loop over ordinary Groovy values is fine when each iteration builds a query rather than reading table rows. Here, the loop creates one filtered table per symbol, and each table is still a live recipe.

Next steps