Morel Cookbook

Problem

I want to write my first Morel query against a small dataset and see output in under a minute.

Example

val orders = [
  { id = 1, product = "Earl Grey",  quantity = 12, region = "north" },
  { id = 2, product = "Espresso",   quantity = 30, region = "south" },
  { id = 3, product = "Darjeeling", quantity = 8,  region = "north" },
  { id = 4, product = "Espresso",   quantity = 50, region = "west"  },
  { id = 5, product = "Earl Grey",  quantity = 6,  region = "east"  }
];

from ord in orders
  where ord.quantity > 10
  yield { ord.product, ord.quantity };

Output:

val it =
  [{product="Earl Grey",quantity=12},{product="Espresso",quantity=30},
   {product="Espresso",quantity=50}] : {product:string, quantity:int} list

What's happening

val orders = [ … ] binds a list of records to the name orders. You didn't write a type, but Morel inferred one — every orders element has id, product, quantity, and region, and the compiler knows their types. The query from ord in orders where ord.quantity > 10 yield { ord.product, ord.quantity } reads like SQL but is a first-class expression in a real programming language, which is the thing this cookbook is about.

Three other things worth noticing. The query returns a list of records, so you can bind the result to a name and query it again — queries compose. Field access is postfix (ord.product) rather than prefix (#product ord), because postfix reads better in pipelines. And the output records have a different type from the input: you dropped id and region, and the compiler knows — see the inferred type after val it.

One gotcha: avoid o as a loop variable. It's Standard ML's function-composition operator, so from o in orders parses as an operator expression and won't compile.

Variations

Filter on a different predicate and yield a single field:

from ord in orders
  where ord.region = "north"
  yield ord.product;
val it = ["Earl Grey","Darjeeling"] : string list

Yield a computed value. bulk is a new field, typed as bool, that didn't exist on the input:

from ord in orders
  yield { ord.product, bulk = ord.quantity >= 20 };
val it =
  [{bulk=false,product="Earl Grey"},{bulk=true,product="Espresso"},
   {bulk=false,product="Darjeeling"},{bulk=true,product="Espresso"},
   {bulk=false,product="Earl Grey"}] : {bulk:bool, product:string} list

Note how Morel sorts record fields alphabetically: bulk comes before product in both the values and the printed type.

See also