A formal language for recipes: brain dump
Posted by Tom Moertel Fri, 08 Jan 2010 05:15:00 GMT
One of the projects I’ve had
in the back of my mind for a few years is a formal language for
recipes. Professional bakers have long used
formulas
to express the core of their recipes. And with Michael Ruhlman’s
Ratio:
The Simple Codes Behind the Craft of Everyday Cooking being so well received by geeky home
cooks, it seems the time may be right to start hacking away.
As a brain dump, here are some syntax ideas I’m playing with. (BTW, if you want to learn how to make a pie from scratch, here you go. Use butter or lard for the fat if you’re serious about pie.)
-- classic recipe for 3-2-1 pie pastry
pie_crust:
3 flour
2 fat, cold
1 water, ice cold
yields 1 dough -- (optional: default result is final item)
flour_fat_mix:
flour
fat
"cut fat into walnut-sized pieces"
"combine in chilled bowl"
"mix until fat is pea sized"
dough:
flour_fat_mix
water
"add water while mixing gently"
"stop when shaggy dough begins to form"
"gently shape dough into portioned rounds by hand"
"wrap portions with film-wrap"
"refrigerate"
-- pie, which uses the crust recipe above
pie_9_inch:
24 oz pie_crust, chilled
1 pie_tin, 9 inch, chilled
1 filling, enough for 9-inch pie
yields 1 pie, 9 inch
bottom_shell:
pie_crust
pie_tin
filling
take pie_crust, 13 oz, "portion dough"
"roll into 11-inch round"
"line pie tin with dough"
"add filling"
top_shell:
pie_crust
take pie_crust, remainder
"roll into 10-inch round"
pie:
bottom_shell
top_shell
"top the pie, sealing edges well"
"trim excess dough from edges, decorating if desired"
"cut vent hole(s) in top"
"refrigerate for at least 30 minutes to set dough"
"bake at 425 degrees F for 15 minutes"
"reduce oven to 350 degrees F"
"bake until done, about an hour"
readers
You clearly need lambda abstraction too, so you can write parameterized recipies :-)
If you had this the eventual caller (user) could vary the diameter of the pie, and the recipe could produces the correct round size and cooking time.
Somehow reminds me of Makefiles. A make utility would be cool to build the recipe. :)
Max, one thing that’s probably not obvious from the examples is that the recipes in the language are all functions. The ingredients are input variables and their given quantities are not absolute but serve as constraints, establishing necessary relationships between ingredients.
In the pie-crust recipe, for example, the quantities 3, 2, and 1 establish the classic 3-2-1 flour-fat-water ratio for pie pastry. Its type might be given as
The sub-recipes within a recipe are also functions, but their inputs are implicitly satisfied by the variables from the surrounding context. In
flour_fat_mix, we have the inferred typeAlso, if you wanted, you could lift
flour_fat_mixout of thepie_crustrecipe to use it in another recipe or on its own. Doing so would release it from the 3-2 flour-fat ratio constraint supplied by thepie_crustcontext, so you would have to supply new constraints (either explicitly or implicitly via context), or you could tell the system to extract the sub-recipe with its original constraints.Clever idea!
In this example, expression of quantities only as ratios (mostly) works because all ingredients are measured in the same way… here, as volumes. But you still have the problem of determining how much of each ingredient is needed if you know how much dough is required; your recipe makefile doesn’t “know” that, yet. (The yield is not, in general, the sum of the ingredients; there are chemical changes and waste streams to consider.)
In the general case, ingredients come in different (incompatible) measures. Besides weight and volume, there are measures like “two medium-sized avocados”. Do you think adding units of measure would make the format too cumbersome?
John, the way it works (in my head) is that recipes represent constraints and relationships, not absolutes, with further constraints and relationships being supplied by the system to represent general knowledge of cooking. The system then combines all of this information, along with additional constraints supplied by the cook wishing to “instantiate” a recipe, to arrive at the final result.
If I get it right, the internal knowledge will be represented as a recipe written in the language itself. This recipe, unlike most, will be large and preloaded into the interpreter. It’s where knowledge about weight-volume equivalences, steam loss, and the like can be represented. Further, the language will allow recipes to be nested, with the constraints of inner recipes shadowing conflicting outer constraints, which should allow cooks to override the standard knowledge base when desired.
The system (as contemplated) already supports units of measure. The recipes in the example actually contain all the information necessary for the system to solve for quantities. The
pie_9_inchrecipe, for example, specifies that it needs 24 oz ofpie_crust(per pie). If you’re making 2 pies, then, the system would known that you need 24 oz of flour, 16 oz of fat, and 8 oz of water for the crusts.BTW, the measurements given in the example recipes are actually weights, not volumes. Having worked in a professional kitchen, I prefer to “scale” dry ingredients. It’s faster and more accurate than measurement by the scoop.
Cheers,
Tom
Tom Moertel:
It’s the well known and vastly described in the literature Recipe monad.
=)
Tom,
I wasn’t as clear as I should have been. I understood that you intended the format to express constraints, and not absolute amounts. My point was that it couldn’t (yet) express all the constraints I thought would be worthwhile, with two specific examples.
It seems you’re content to use weight measures exclusively, and that certainly simplifies things. (Most of my kitchen experience is decidedly unprofessional, so I’ll believe you if you say that’s practical.)
But I think there’s a good case for specifying the quantity of the product, relative to the ingredients. Recipes compose; if I have recipes for A->B, and (B,C)>D. I should be able to form (A,C)>D by composition, but I need to have the ratio of B to A so I can calculate the ratio of C to A. Do you see why I claim that you can’t do that with your format, until you somehow declare the ratio of products to ingredients?
If instead of “yields 1 dough” (where the 1 carries no value), you write “yields 6 dough” (giving output weight relative to ingredients), then you get composition. You can also infer needed ingredient quantities from the desired quantity of product. To me that feels like a big gain for a small change.
John,
Let me clarify. The design, as least in my head, solves the problems you mention.
First, the system doesn’t assume or require that you use weights. I just prefer to do so and defined my recipes accordingly. The general knowledge of cooking that the interpreter preloads will include weight-to-volume equivalences (e.g., 5/16 lb flour ~ 1 cup flour); the system will be able to use volume measurements, should the cook prefer to measure that way.
Second, the system, as designed, does track the output quantity and output-to-input ratios. It’s just that rather than quantifying the output as “6 dough” I quantified it as “1 dough” because the pie-crust recipe yields one batch of dough. That is, in the chef’s mind, if you combine 3 parts flour, 2 parts fat, and 1 part water, you get 1 batch of pie-crust dough, not 6 parts – even though 1 batch is the same as 6 parts. From this, the system infers that 1 batch equals 6 parts and attaches a one-sixth scaling factor to to the recipe: 1 part = 1/6 recipe batch.
For example, if you wanted to make two 9-inch pies, the system would reason as follows:
Cheers,
Tom
Tom,
Thanks for your latest reply. It does clear up quite a bit about your design.
Unless I’m missing something, you won’t be able to accurately infer the quantity of product. In message 4 above I wrote:
> (The yield is not, in general, the sum of the ingredients; there are chemical changes and waste streams to consider.)
Consider evaporation; absorption; release of CO2 or other gases; pouring-off wastewater; material left behind in a pan. The product will often weigh much less than the sum of its ingredients, and it could conceivably weigh more.
Thanks for the “food for thought”.
John
John,
Thanks for your comment.
If the yield of some recipe is not the sum of its ingredients, and if the difference between the exact truth and the sum approximation matters, the recipe language allows for authors to specify waste in the yield. For example, here’s a simple recipe for boiling away half of a liquid:
In most cases, however, the difference doesn’t matter, being already factored into cooking lore and the specifics of recipes.
Cheers,
Tom
FYI -
http://www.anthus.com/Recipes/CompCook.html
also:
http://www.dangermouse.net/esoteric/chef.html
Frankly I don’t think that you can fully formalize a recipe, but best of luck.
I think that the recipe tree here: http://www.anthus.com/Recipes/tree.gif is a good starting point for a Recipe ADT. See cookingforengineers.com for a interesting UI for this tree-based recipe format, for example at the bottom of http://www.cookingforengineers.com/recipe/230/Macaroni-and-Cheese-Bake
nice and interesting stuff