Morel Cookbook

Problem

I have a list of orders and I want the ones that meet two or three conditions. Same shape as a SQL WHERE with AND, OR, NOT — but as a first-class expression rather than a statement.

Setup

The shared tea-and-coffee importer dataset. Every recipe in this section uses it.

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 = 3,  productId = 103, quantity = 8,  customer = "Bramble Cafe",      region = "north", date = "2025-09-05", status = SOME "fulfilled" },
  { id = 4,  productId = 204, quantity = 50, customer = "Ironbridge Hotel",  region = "west",  date = "2025-09-07", status = SOME "fulfilled" },
  { id = 5,  productId = 101, quantity = 6,  customer = "Kiln & Crumb",      region = "east",  date = "2025-09-10", status = SOME "cancelled" },
  { id = 6,  productId = 202, quantity = 20, customer = "Granary Foods",     region = "south", date = "2025-09-12", status = SOME "fulfilled" },
  { id = 7,  productId = 103, quantity = 14, customer = "Harbour Roastery",  region = "west",  date = "2025-09-14", status = SOME "fulfilled" },
  { id = 8,  productId = 102, quantity = 25, customer = "Ironbridge Hotel",  region = "west",  date = "2025-09-18", status = SOME "fulfilled" },
  { id = 9,  productId = 201, quantity = 40, customer = "Bramble Cafe",      region = "north", date = "2025-09-21", status = SOME "cancelled" },
  { id = 10, productId = 103, quantity = 9,  customer = "Kiln & Crumb",      region = "east",  date = "2025-09-24", status = SOME "fulfilled" },
  { id = 11, productId = 202, quantity = 16, customer = "Harbour Roastery",  region = "west",  date = "2025-09-27", status = NONE },
  { id = 12, productId = 101, quantity = 22, customer = "Granary Foods",     region = "south", date = "2025-09-30", status = SOME "fulfilled" }
];

Example

Bulk orders outside the north region. andalso is the same as && — both predicates must be true for a row to survive.

from ord in orders
  where ord.quantity >= 20 andalso ord.region <> "north"
  yield { ord.id, ord.customer, ord.quantity };
val it =
  [{customer="Granary Foods",id=2,quantity=30},
   {customer="Ironbridge Hotel",id=4,quantity=50},
   {customer="Granary Foods",id=6,quantity=20},
   {customer="Ironbridge Hotel",id=8,quantity=25},
   {customer="Granary Foods",id=12,quantity=22}]
  : {customer:string, id:int, quantity:int} list

What's happening

where is a filter step. Think of from … where … yield as the pipeline SQL can't quite write: each clause is an expression that takes a collection and returns one. The predicate can be any boolean expression Morel knows how to check — equality, comparisons, andalso, orelse, not, and any function you've defined that returns bool.

Two things to keep straight. First, = in Morel is equality, not assignment, so where ord.region = "north" reads correctly in both SQL and Morel senses. <> is "not equal". Second, short-circuiting: andalso and orelse evaluate left-to-right and stop as soon as the answer is known, so put the cheaper predicate first if one is expensive.

A SQL equivalent for the example, for comparison:

select id, customer, quantity
from orders
where quantity >= 20 and region <> 'north';

Variations

orelse to widen the net — either predicate is enough:

from ord in orders
  where ord.region = "north" orelse ord.region = "east"
  yield { ord.id, ord.region };

not wraps any boolean expression — here it negates a substring match, which is Morel's answer to SQL's NOT LIKE '%Cafe%':

from ord in orders
  where not (String.isSubstring "Cafe" ord.customer)
  yield ord.customer;

See also