Just finished my own overthinking of recipe structures. I figure that a recipe is more or less an upside-down tree!
Where you start with a list of all the nodes (ingredients)
Have a n:1 relationship with the next series of nodes (steps)
until you finish at a single node (the dish you're trying to make)
So instead of having a separate chunk of "here's my ingredients" and "let me repeat the ingredients and one by one instruction until the end" I figure you can display the upside-down tree to convey more information with less words.
An example being https://cookbook.cstebbins.com/recipe/bul-koki
With the underlying tree structure looking like https://assets.cstebbins.com/cookbook/images/bulkokiTree.png
Well, it would be more like a directed acyclic graph, not a tree, because you can re-use nodes.
(And it's not even necessarily acyclic, because eg sourdough or master stock is perhaps best modelled as a cycle.)
In any case, looking at a recipe as a tree or a graph is a very limited view:
A great recipe isn't a list of steps to produce 'something'. A great recipe has a dish in mind with a specific taste (or a specific family of tastes with knobs you can turn), and the author has worked out what parts of the recipe you need to adapt in what ways to compensate or take advantage of current local conditions.
Eg some days it's warmer than others, so you need to adjust your fermentation times. Or some parts of the world have harder water than others. Or your available veggies might have different amounts of moisture etc.
From a slightly pedantic point of view that misses the point of cooking or baking, I disagree about the ability to re-use nodes.
If you have a recipe that uses 150g flour in step 1, then later uses 350g flour in step 4, then you actually have two separate ingredients:
1. 150g plain flour
2. 350g plain flour
It may useful to present an ingredient summary for the purposes of shopping which collects that to 500g flour, but from when trying to represent the recipe, it's actually two different ingredients, and it'd be a false re-use to try to re-use the node or try to treat those ingredients as a single ingredient of greater quantity.
Just because two things appear otherwise identical, doesn't mean they are.
I sometimes see a similar issue in software development and re-use of classes or functions. Two different cases happen to need identical handling, so get put through the same code paths. Then later it turns out one case needs to be treated slightly differently, so that path ends up with special casing, and you get a mess of special handling with case statements or "bool treatDifferently" parameters.
If things are conceptually different, then treat them differently, don't assume false commonality.
Well, you need to be careful what your graph and nodes are supposed to model.
I was implicitly assuming that your graph models instructions and procedures. You seem to assume that the graph models concrete physical items.
You can execute the same steps twice, but you can't eat the same piece of butter twice.
It's a bit philosophical, but I'm not sure you can really execute the same steps twice. A cycle would imply you can get stuck baking something forever.
When you come back to do the "same" operation again, you're fundamentally dealing with different items, even if it doesn't appreciably appear to be the case.
You might have different criteria for evaluation, but something has fundamentally changed.
Let's look at the simplest case you might be tempted to model as a cycle:
> "Salt to taste"
Now, that would traditionally be modelled in a flowchart as a cycle:
1. Add salt
2. Test taste. Too little salt? Go to step 1. Else go to step 3.
3. End.
However, in strict modelling terms, I'd argue there's a hidden parameter, "number of saltings attempted".
You wouldn't repeat that a hundred times and keep adding salt, after a while you'd suspect that your taste-buds had gone or the salt had gone funny.
So rather than a cycle, it's actually a series of steps with a hidden step counter, which eventually has different outcomes.
I'm glad you figured out that the graph is a model, and like any good model, it picks some characteristics of reality to keep and many to leave out.
Have a look at the example of a turnstile state machine on Wikipedia: https://commons.wikimedia.org/wiki/File:Turnstile_state_mach...
The graph of that state machine suggests that you repeat the cycles indefinitely. But I have it on good authority that real world turnstiles only have a finite coin box. [citation needed] So there's some extra hidden state.
You'll also need to break up intermediate products. An egg breaks down into yolk and whites. Whites may be used for different purposes, one of them foam. Foam might be broken down further.
Recipes are not a tree.
See my comment in another thread about this topic:
https://news.ycombinator.com/item?id=41700416
In particular the lemon meringe pie. You separate whites and yolks to prepare the filling and the meringue separately before merging them in the last step.
>it'd be a false re-use
Technically, recipes are monoidal pre-orders and I guess you could find arguments against what you propose in the mathematical foundation, unfortunately I'm not versed enough in category theory to articulate it properly.
Separating the eggs is a great example!
> From a slightly pedantic point of view that misses the point of cooking or baking, I disagree about the ability to re-use nodes.
For example, if you make the "Lobster, Poached with Shellfish, Pear, and Kale" from the 11-madison park cookbook, you will make a "white balsamic vinaigrette" which is then used directly to make the "Pear Glaze", "Pickled Mustard Seeds", and "Pear marmalade" components. (The pickled mustard seeds also go into the "Pear marmalade" component.)
> if you make the "Lobster, Poached with Shellfish, Pear, and Kale" from the 11-madison park cookbook, you will make a "white balsamic vinaigrette"
I think the post you're replying to is saying that you'll make 3 "white balsamic vinaigrettes" instead of reusing the same one 3 times.
Which makes sense since one step of the recipe will be to divide the vinaigrette into 3 amounts to make the other ingredients.
No, you make a larger amount of liquid, and then you divide it.
You'd only have one node. Just like you don't put every bite of food into its own node.
you can divide it into multiple containers, where they become multiple objects, each participating in a different (sub)recipe. node as use of unit-item, not the physical bunch of item itself. instance, not type.
Depending on what you want to model, either view can make sense.
> (And it's not even necessarily acyclic, because eg sourdough or master stock is perhaps best modelled as a cycle.)
So what you are saying is that cooking instructions need phi nodes?
This is just like the Cooking for Engineers recipe format. If you haven't seen it, click on any recipe and scroll down to the bottom.
https://www.cookingforengineers.com/
that's interesting! Same concept. I initially got the idea from the book Cooking for Geeks https://www.cookingforgeeks.com/
I wonder if they got it from Cooking for Engineers
Except the completion of the preparation of various nodes may have to be synchronized to that of other nodes - i.e. the steps can not be parallelized in the general case. Some nodes expire, experience changes in temperature or moisture distribution and so on. In that sense, there is additional context that your model doesn't account for that may preclude certain nodes from being combined.
How often do you see that knowledge expressed in the recipes themselves?
Microdata doesn't need to _fully_ describe something. It's not describing to a machine how to perform the recipe, it's just annotating the text with useful metadata. It's metadata, doesn't need to be full data.
Doesn't mean that there aren't users that would desire a GANTT chart for a recipe (or set of them) if given the opportunity from enough data. Getting an entire meal out on time is one of the oldest schools of project management, after all.
I don't think this knowledge even exists. Can you point me to a recipe website that has any structured information about paralellized steps?
To me, it sounds like you want to annotate data that doesn't even exist.
Well, it’s often in the steps; stuff like, while the sauce is simmering, prepare the vegetables… just before the sauce has reduced, add the red wine. And some is just implicit knowledge that comes with experience, true.
I also haven’t seen a site that actually turns that into a neat visualisation, but as an amateur chef, properly timing steps of a recipe is definitely important.
I’m inclined to agree with GP though that a good model should be able to capture that.
To me it looks like having the timing for each step is enough (schema.org already supports that).
You can then _decide_ what to paralellize (maybe you want to take it slow, maybe you have another person to help you, etc).
To me, it sounds way simpler. Specially for those writing or annotating the recipes. You're thinking of the reader, but the writing is important too, it's the hidden cost of making super detailed models.
Some recipes end up with more than one dish, like when you prepare a thing and its accompanying sauce together.
I guess superficially you can join them together with a final node that is like "serve all the dishes".
You also often have ingredients or intermediate products that are used in more than one step so a tree is not ideal. You could duplicate those ingredients/intermediates but that would not be helpful.
And for more complex meals, time planning becomes more important so that everything is ready to serve together.
To be honest, I kind of prefer when a recipe treats duplicate ingredients as completely separate. Rather than that "use 300g of the butter here, use the rest here" approach which is harder to follow.
That works well for a basic off-the-shelf ingredient, but what if you needed a complex dressing/sauce/broth/marinade/spice mix that was later used in multiple combinations. You'd want to make that all in one batch.
Some dishes require more overlap, which will be tricky to write in this format.
E.g. taking the pan juices and turning that into a sauce. It's a separate process/dish, yet wholly dependant on the other process completing a critical step.
What if the same ingredient is used twice in a recipe, for example for cooking and finishing?
Here is an example from Cooking for Engineers - Lemon Bars. If you scroll down to the recipe you can see it uses all-purpose flour twice and lists it two times on the left side of the table.
https://www.cookingforengineers.com/recipe/33/Lemon-Bars
This makes it real hard to determine the total amount of flour needed. Maybe add another column to the left, that sums up all the ingredients, in the format you might get it at the store. Then the second column becomes the mise en plas step, where the base ingredients are divided and prepared/chopped/grated etc.
Problem is column 1 and 2 wont necesarily have ingredients on the same row...
Left columns is raw ingredients no? LLM did perfect job with summing it. This is like instruction plate not shopping list. Can transform trivially.
I think of them as 2 different nodes, 2 instances of the same type of ingredient.
Practically I just have it listed twice if it's important like butter.
https://cookbook.cstebbins.com/recipe/the-best-swedish-meatb...
otherwise if it's a staple like salt I just say something like "stir in and season with additional salt and pepper to taste"
like in https://cookbook.cstebbins.com/recipe/sloppy-sophisto-joes
> I think of them as 2 different nodes, 2 instances of the same type of ingredient.
But this is inconvenient during the shopping step of the process. There you want all uses of an ingredient (possibly across multiple recipes) collapsed into 1 node.
for sure! The use case I'm solving for myself is easy lookup while I'm cooking which is different than the use case of me going shopping
Maybe someday I'll add a feature to grab a shopping list. Could organise it into sections of the store and do a quick traveling salesman problem :D
also due to the conciseness of the grid, it's also not great if you're making it for the first time
> The use case I'm solving for myself is easy lookup while I'm cooking
This is a constant source of stress for me cooking from cookbooks I otherwise like. I prefer to measure on-demand, and wish the steps would just duplicate the quantities of the ingredients "whisk in the olive oil (1/4 cup)" instead of "whisk in the olive oil" so I don't have to scan the list of ingredients and figure it out
Duplicating the nodes is also far from ideal when the reused node is not a raw ingredient but an intermediate with possibly complex dependencies itself. Duplicating the whole subtree would not be helpful.
What if there was one further section in the left column that runs across the y axis and tallies ingredients?
That way there are three "segments" of the spreadsheet: shopping; prep; cooking.
I would say that it gets a bit more complex than that.
some recipes can produce multiple related dishes, and some ingredients can have multi-step substitutions. So Graph would be a better representation.
you could join multiple dishes into final 'abstract' dish to force it into tree-esque shape with some shuffling hopefully, but i think that's unnecessary limitation - especially if you consider that some recipes give you some compound ingredients(stocks for example) as side-output that you might want to use in future!
This is a great case for using that information for planning future dishes you could cook for free.
100%
under the hood I have it more like a graph with levels. The traversal is from all of the "leaves" to a single node rather than from the head node towards the leaves as in a traditional tree. The upside-down tree is more for a visual for the reader.
At the moment I don't have a recipe that outputs 2 end states but entirely possible since it's more of a graph under the hood.
I experimented a bit with that format and ChatGPT can generate pretty decent results. I described it in a blog https://cooklang.org/blog/03-ai-and-the-evolution-of-recipe-.... Here's a full graph it generated https://cooklang.org/blog/full-recipe-graph.png. I like how it was able to name intermediate nodes.
Interesting. Also allows for some fun ‘expansion’ in recipes. A leaf node may be ‘chicken stock’ but you could link to a recipe that boils down to that one ‘chicken stock’ node if you want to DIY it.
In a previous job I worked for a site doing natural language parsing on recipes.
We noticed one of our partner websites had an unusual number of unique ingredients. It turned out every ingredient was a link to another recipe to make that ingredient, along the lines of your idea.
However for some reason (presumably SEO) they took this to the extreme and everything was a recipe. Including apples.
The recipe for “apple” is
1. Take 1 Apple
2. Eat and enjoy
But since Apple is a prerequisite for this recipe, infinite looping is a risk in the kitchen now
overthinking some more: your idea is great but it removes the one feature that classic recipes have: sequencing.
You can have the best of both world, though, if the branches in your tree had some kind of timing associated.
Like when you make lasagnas, the bolognese takes 1 hour to cook, while the bechamel takes less than 10 minutes. You need to have a way to say: "while the bolognese is reducing, make the bechamel"
I’ve been cooking with recipes quite a bit lately (before it was just random stuff) and I must say that sequencing is always wrong.
Unfortunately they all seem to assume that chopping, cleaning and moving stuff around is all done in 0 time. I wish the recipes would take this into account more.
Because of this I actually would prefer the plain tree organisation more.
mise in place is the art of reducing all that other stuff to as close as zero time as possible during the cooking, and many recipes expect the cook to read through first and identify what can be prepared/placed in advance. https://en.wikipedia.org/wiki/Mise_en_place
That is fine for professional recipes, when you cook for dozens of people. But for home cooking, if they say 25-35 minutes prep time 40-60 minutes cooking time, I’d expect them to count the cutting in there.
I'd start with a DAG.
In fact, going through a project management basics training at work recently reminded me that cooking recipes are isomorphic to project plans. Which makes DAGs an obvious natural representation for a recipe, and incidentally, makes the Gantt chart one of the best ways of looking at it.
A Gantt chart for a single recipe may make some sense, but doesn’t really add value. A Gantt chart for a whole dinner — I’m making these six dishes, figure out what I have to do when — is much more interesting. But for this to be passive, the edges on your DAG need to carry metadata about how long the action can/must/should be delayed (no active work), and the nodes (tasks) must carry metadata on how long the task takes. For example, after a whipping my egg whites, they can’t just sit there for two hours; and boiling my pasta water may take at least 12 minutes, but I’d rather not leave it on the boil for another 30.
> the edges on your DAG need to carry metadata about how long the action can/must/should be delayed (no active work), and the nodes (tasks) must carry metadata on how long the task takes.
Yes, those are all constraints that need to be stored there. Some of them are fixed, others can be computed when optimizing the plan.
It's also information you should have specified on the recipe. Most of it usually isn't, as it's either obvious[0] or the recipe author doesn't even realize it's a parameter.
--
[0] - Some obvious to anyone, some obvious to someone very experienced at cooking. Confusing them makes things really difficult for beginners.
Wow, all recipes should look like that, nice work!
Ha, your example makes intuitive sense right when you see it. Really clever thanks for sharing it!
thanks for checking it out!
Of course, when thinking of actual trees, it would resemble a right-side up tree.
ofc, trees