Problem
I want a narrower shape than what I started with. Drop columns I don't need, rename one or two so the result reads well, and maybe compute a new column from the ones I have — all in a single pass.
Setup
val orders = [
{ id = 1, productId = 101, quantity = 12, customer = "Bramble Cafe", region = "north", date = "2025-09-02", status = SOME "fulfilled" },
{ id = 2, productId = 204, quantity = 30, customer = "Granary Foods", region = "south", date = "2025-09-03", status = SOME "fulfilled" },
{ id = 4, productId = 204, quantity = 50, customer = "Ironbridge Hotel", region = "west", date = "2025-09-07", status = SOME "fulfilled" }
];
Example
Rename two fields and derive a third, dropping everything else:
from ord in orders
yield { orderId = ord.id, placed = ord.date, bulk = ord.quantity >= 20 };
val it =
[{bulk=false,orderId=1,placed="2025-09-02"},
{bulk=true,orderId=2,placed="2025-09-03"},
{bulk=true,orderId=4,placed="2025-09-07"}]
: {bulk:bool, orderId:int, placed:string} list
What's happening
yield builds a new record from whatever you put in the braces. It
accepts three moves in one go — and that's the point of the pipeline.
Projection: list the fields you want and the rest are dropped.
Renaming: orderId = ord.id binds the new name to the old value.
Computation: bulk = ord.quantity >= 20 introduces a field that
didn't exist on the input, with a new type (bool).
The output record's type is inferred from the fields you yielded, and it's a different type than the input. That's what "queries compose" means in practice — the next step in a pipeline sees only the columns you chose to keep, and the type checker knows it.
Two spellings to remember. ord.quantity is the everyday form and
looks like SQL. The shorthand { ord.quantity, ord.customer } — no
explicit name on the left — keeps the field name unchanged. Use it
when rename would be noise. Either way, the yielded record's fields
are sorted alphabetically in Morel's printed output, as you'll see in
every result block.
Variations
Shorthand projection when you want to keep names:
from ord in orders
yield { ord.customer, ord.quantity };
Compute a value from a single input field. Output is two columns —
the passthrough id and the derived revenueSlot:
from ord in orders
yield { ord.id, revenueSlot = ord.quantity * 10 };
See also
- Recipe 07 — Filter rows — the
wherestep that typically sits above this one. - Recipe 01 — First query — where
yieldfirst appears, with a single computed field. - Recipe 10 — Group and aggregate — when projection gives way to summary.