Application Tutorial:
a Restaurant app
Part III

  1. Introduction
  2. Extensions and GUI annotations
  3. State machines
  4. Advanced references
  5. More advanced references
  6. The End

Introduction

This is the third part of the application language tutorial that builds a restaurant app. Complete Part I and Part II before continuing.

So far the model describes what the restaurant has: a menu, tables, orders, and the values computed from them. Part III describes what the restaurant does. An order line travels from ‘ordered’ to ‘served’, an order is closed once everything is served and paid, service staff need to know which dish goes to which table first, and the kitchen assembles dishes from products that are themselves assembled from other products.

Four topics carry that story:

The model grows considerably in this part. Use the tutorial folder listed at the end of each topic when your model and the text no longer match.

Extensions and GUI annotations

In Conditional expressions the Total of an order was computed inside the stategroup Discount applicable, which is why the app shows it in a box together with Discount period and Discount. Both visually and model-wise, Total belongs to the order itself. Move it out, so that the tail of the Orders collection reads:

'Orders': collection ['Order'] {
	...
	'Discount applicable': stategroup (
		'Yes' {
			'Discount period': text -> ^ ^ .'Management'.'Discount periods'[]
			'Discount': number 'eurocent' = switch ^ .'Subtotal' compare ( >'Discount period'.'Minimal spendings' ) (
				| <  => 0
				| >= => product ( from 'percent' ( >'Discount period'.'Percentage' ) as 'fraction', ^ .'Subtotal' )
			)
		}
		'No' { }
	)
	'Total': number 'eurocent' = switch .'Discount applicable' (
		|'Yes' as $'discount' => sum ( $'discount' ^ .'Subtotal', - $'discount'.'Discount' )
		|'No'  => .'Subtotal'
	)
	'VAT': number 'eurocent' = product ( from 'percent' ( ^ .'Management'.'VAT percentage' ) as 'fraction', .'Total' )
}

Total now switches on the state of Discount applicable, and VAT simply uses Total.

The restaurant processes need two more stategroups: Line status on Order lines, and Order status on Orders. The head of the Orders collection becomes:

'Orders': collection ['Order'] {
	'Order': text
	'Order type': stategroup (
		'Takeaway' { }
		'In-house' {
			'Table': text -> ^ ^ .'Management'.'Tables'[]
		}
	)
	'Order lines': collection ['Order line'] {
		'Order line': text
		'Item': text -> ^ ^ .'Menu'[]
		'Amount': number 'units'
		'Line total': number 'eurocent' = product ( .'Amount' as 'units', >'Item'.'Selling price' )
		'Line status': stategroup (
			'On hold' { }
			'Placed' { }
			'Service' { }
			'Served' { }
		)
	}
	'Order status': stategroup (
		'Open' { }
		'Closed' { }
	)
	'Subtotal': number 'eurocent' = sum .'Order lines'* .'Line total'
	...
}

Customers change their mind while ordering, so order lines should not go to the kitchen one by one; they are placed together once everybody has decided. Line status supports that with four states:

Order status has two states: Open from the moment the first line is placed, and Closed after the bill is paid.

The command Place new order has to set these states for the order and its lines:

'Place new order': command {
	...
} as $'param' => update .'Orders' = create (
	'Order' = $'param'.'Provide an order number'
	'Order type' = switch $'param'.'Where is the meal consumed?' (
		|'Outside of restaurant' => create 'Takeaway' ( )
		|'At the restaurant'     as $ => create 'In-house' (
			'Table' = $ .'Where is the customer seated?'
		)
	)
	'Order lines' = walk $'param'.'Order lines'* as $ (
		create (
			'Order line' = $ .'Provide an order line number'
			'Item' = $ .'Item to be consumed'
			'Amount' = $ .'Amount of this item'
			'Line status' = create 'Placed' ( )
		)
	)
	'Order status' = create 'Open' ( )
	'Discount applicable' = switch $'param'.'Apply discount?' (
		|'Yes' as $ => create 'Yes' (
			'Discount period' = $ .'Discount period'
		)
		|'No'  => create 'No' ( )
	)
)

Apart from Apply discount?, the command needs no new parameters: the states Placed and Open follow from the purpose of the command itself, so they are created without asking the user anything.

Order and line status

Add @default: auto-increment to the key attributes of Orders and Order lines:

'Order': text @default: auto-increment
...
'Order line': text @default: auto-increment

This is a GUI annotation: an instruction to the generated user interface, always written with an @ prefix. This one fills in the next free number when a user adds an order or an order line, which saves typing for the rest of the tutorial. GUI annotations deserve a topic of their own; the documentation lists them all.

Finally, group Menu, Orders and Place new order in a new group Service, next to Management.

Notice how cheap these changes are: moving a block of code, adding a derivation, regrouping attributes. The compiler reports every place that a change affects, so a model can be reorganized without putting the data at risk.

<tutorial folder: ./_docs/tutorials/restaurant1/2026.2/step_07/>

State machines

Line status exists now, but nothing changes it yet. In the restaurant, the status follows a fixed course: service takes an order at a table, customers may still change it, the lines are placed for preparation, and the prepared lines are served. A line should never skip a step — a dish cannot be served before it is prepared.

A stategroup whose states change one step at a time is a state machine, and commands are what move it forward:

'Line status': stategroup (
	'On hold' {
		'Place order line': command { } => update ^ .'Line status' = create 'Placed' ( )
	}
	'Placed' {
		'Ready for service': command { } => update ^ .'Line status' = create 'Service' ( )
	}
	'Service' {
		'Served': command { } => update ^ .'Line status' = create 'Served' ( )
	}
	'Served' { }
)

Each state gets a command that moves it to the next one. These commands take no parameters, because they need no input: the current state determines what happens.

Build the model, open Orders in the app, select order 001, and open its first order line: Place order line commands Below Line status, the button Place order line appears (a line you entered yourself starts in state On hold). Click it: the line moves to Placed, the button disappears, and Ready for service takes its place. Click that one too and watch the status advance.

The buttons are exactly the operations that a state allows, which is also the basis for permissions: different roles in the restaurant — service, kitchen, management — can be given access to different parts of the model, so that each of them sees only the buttons that belong to their job. The Users & Authentication guide is the starting point for that.

Clicking every line separately is tedious, so add a command that places all lines of an order that are still On hold:

'Place order lines': command { } => walk .'Order lines'* as $'line' (
	switch $'line'.'Line status' (
		|'On hold' => update $'line' (
			'Line status' = create 'Placed' ( )
		)
		|'Placed'  => ignore
		|'Service' => ignore
		|'Served'  => ignore
	)
)

This command goes below Order lines, inside Orders. Its implementation reads: do this (=>) — walk the collection Order lines, which visits every node in it, and store each node under the name $'line'. For each of those nodes, switch on Line status: in state On hold, update the node by creating the state Placed; in any other state, ignore it and do nothing.

In short: walk all order lines, and set the lines that are On hold to Placed.

Build the model and open order 001 again: Place order lines command Click the button and refresh the order lines. Only the lines that were On hold changed.

The last part of the process: when the customer leaves, all lines have to be served and the bill has to be paid before the order can be closed. Add these lines to the state Open of Order status:

'Order status': stategroup (
	'Open' {
		'All served': stategroup = switch ^ .'Order lines'* .'Line status'?'On hold' (
			| none  => switch ^ .'Order lines'* .'Line status'?'Placed' (
				| none  => switch ^ .'Order lines'* .'Line status'?'Service' (
					| none  => 'Yes' ( )
					| nodes => 'No' ( )
				)
				| nodes => 'No' ( )
			)
			| nodes => 'No' ( )
		) (
			'No' { }
			'Yes' {
				'Paid': command { } => update ^ ^ .'Order status' = create 'Closed' ( )
			}
		)
	}
	'Closed' { }
)

How does the model determine that all lines are served, without knowing how many lines an order has? By checking that no line is in any of the other three states. Note the difference between switching on a state and switching on the existence of nodes:

(^ steps are left out of these two lines, so that they can be compared directly.)

The second line reads: take all nodes (*) of Order lines, keep those whose Line status is On hold, and switch on the result — either there are such nodes (nodes) or there are none (none). When there are none, the same check follows for Placed and for Service. Only when all three checks yield none are all lines served, because every line always has exactly one of the four states.

Once All served is Yes, the model offers a button for the moment the customer pays, which sets Order status to Closed: All served? All served!

<tutorial folder: ./_docs/tutorials/restaurant1/2026.2/step_08/>

Advanced references

Service staff need more than a list of prepared lines: they need to know which line to take first, and where to bring it. Add that information in two steps, starting from the overview of Order lines with the view set to Full: Overview order lines

First, a Priority in the state Service of Line status:

'Service' {
	'Priority': stategroup = switch ^ >'Item'.'Item type' (
		|'Beverage' => 'Low' ( )
		|'Dish'     as $'dish' => switch $'dish'.'Dish type' (
			|'Appetizer'   => 'Medium' ( )
			|'Main course' => 'High' ( )
			|'Dessert'     => 'Low' ( )
		)
	) (
		'Low' { }
		'Medium' { }
		'High' { }
	)
	'Served': command { } => update ^ .'Line status' = create 'Served' ( )
}

This resembles the derivation of All served in the previous topic, except that it switches on the state of a stategroup instead of on the existence of nodes. To keep the model simple: desserts get a low priority (mostly cold), appetizers a medium one, and main courses a high one (mostly warm food that should not wait). The result is an extra column: Priority

Second, the table to serve to — but only for In-house orders, and only when the line is ready for service. That means looking at the states of two stategroups at once, Order type and Line status:

'To serve': stategroup = switch ^ .'Order type' (
	|'Takeaway' => 'No' ( )
	|'In-house' as $'in-house' => switch .'Line status' (
		|'On hold' => 'No' ( )
		|'Placed'  => 'No' ( )
		|'Service' => 'Yes' ( 'Table' = $'in-house'>'Table' )
		|'Served'  => 'No' ( )
	)
) (
	'No' { }
	'Yes' {
		'Table': text -> ^ ^ ^ ^ .'Management'.'Tables'[] = parameter
	}
)

This shows the table when To serve is Yes: To serve table

The structure is familiar — state switches on stategroups — but the parentheses after the state Yes are new. They declare a state parameter: a piece of information that a state carries, comparable to a command parameter. Here the state Yes carries the Table of the order.

The node of state In-house is stored as $'in-house', and a few lines further down its Table reference is passed to the state parameter. Inside the state Yes, the text property Table is then derived from that parameter. Its declaration also states which collection the text refers to: the collection Tables in the group Management. That reference closes the loop: the value derived from the parameter must come from the same collection that Table refers to. The compiler checks this while building, so pointing at a different collection by accident is caught immediately, instead of producing an app with dangling references.

Now suppose the priority should also be visible when To serve is Yes. The model this produces is not clean — the priority ends up in the table twice — but the structure it needs is worth seeing.

The priority lives on the node of state Service of Line status, so that node has to be reachable. Store it as $'service' in the state switch of To serve:

'To serve': stategroup = switch ^ .'Order type' (
	|'Takeaway' => 'No' ( )
	|'In-house' as $'in-house' => switch .'Line status' (
		|'On hold' => 'No' ( )
		|'Placed'  => 'No' ( )
		|'Service' as $'service' => 'Yes' ( 'Table' = $'in-house'>'Table' )
		|'Served'  => 'No' ( )
	)
) (
	...
)

Building this model fails: the compiler cannot find the named object in-house. $'service' was stored one level below $'in-house', and at that level only the nearest named object is visible. Think of the named objects as notes stacked on top of each other: only the top one can be read. Reaching a named object from a higher level takes a step up — not the regular ^, which steps up in the data, but $^, which steps up through the named objects. Their names, by the way, exist for the reader only; the compiler is just as happy with $.

With that step added, the model builds:

'To serve': stategroup = switch ^ .'Order type' (
	|'Takeaway' => 'No' ( )
	|'In-house' as $'in-house' => switch .'Line status' (
		|'On hold' => 'No' ( )
		|'Placed'  => 'No' ( )
		|'Service' as $'service' => 'Yes' ( 'Table' = $^ $'in-house'>'Table' ) // <--- !! $^
		|'Served'  => 'No' ( )
	)
) (
	...
)

It builds, but $'service' is not used yet, so put it to work:

'To serve': stategroup = switch ^ .'Order type' (
	|'Takeaway' => 'No' ( )
	|'In-house' as $'in-house' => switch .'Line status' (
		|'On hold' => 'No' ( )
		|'Placed'  => 'No' ( )
		|'Service' as $'service' => 'Yes' where 'Service' = $'service' ( 'Table' = $^ $'in-house'>'Table' ) // <--- !! where ...
		|'Served'  => 'No' ( )
	)
) (
	'No' { }
	'Yes' where 'Service' -> .'Line status'?'Service' { // <--- !! where ...
		'Table': text -> ^ ^ ^ ^ .'Management'.'Tables'[] = parameter
	}
)

The keyword where declares a reference rule: it narrows down what a reference may point at. Here it further constrains the state Yes, which now holds a reference to a node of state Service.

Zooming out on the structure:

'To serve': stategroup = switch ^ .'Order type' (
	...
	|'In-house' as $'in-house' => switch .'Line status' (
		...
		|'Service' as $'service' => 'Yes' where 'Service' = $'service' ( 'Table' = $^ $'in-house'>'Table' )
		...
	)
) (
	...
	'Yes' where 'Service' -> .'Line status'?'Service' {
		...
	}
)

It contains:

The node of state Service is now available inside the state Yes, so Priority can be derived from the Priority of that node:

'Yes' where 'Service' -> .'Line status'?'Service' {
	'Table': text -> ^ ^ ^ ^ .'Management'.'Tables'[] = parameter
	'Priority': stategroup = switch .&'Service'.'Priority' (
		|'Low'    => 'Low' ( )
		|'Medium' => 'Medium' ( )
		|'High'   => 'High' ( )
	) (
		'Low' { }
		'Medium' { }
		'High' { }
	)
}

A reference rule is addressed with the &-symbol: .&'Service' .'Priority' reads the stategroup Priority of the node reached through the Service rule, and the rest is an ordinary state derivation.

Why the &? Reference rules are more often used on a text property, as in this example:

'Electric vehicles': collection ['Vehicle'] {
	'Vehicle': text
	'Color': text
	'Is': stategroup (
		'Fast' {
			'Top speed': number 'km/h'
		}
		'Slow' { }
	)
}

'Car': text -> .'Electric vehicles'[] as $
	where 'fast' -> $ .'Is'?'Fast'

'Top speed': number 'km/h' = .'Car'&'fast' .'Top speed'

The property Car has a reference rule fast — a Car can only be an electric vehicle that is also Fast — and Top speed is derived through that rule, written as .'property'&'where': first the property, then the rule. A state is not a property and has no property name to put in front, so for a state the notation shortens to .&'where'.

The result in the app: Table and priority

The example is contrived on purpose: it shows, in a few lines, how far a model can reach into its own structure when a real application needs it.

<tutorial folder: ./_docs/tutorials/restaurant1/2026.2/step_09/>

More advanced references

The kitchen has not been modeled yet. It is where ingredients become dishes: basic ingredients have to be in stock, and their purchase prices determine what a dish costs.

Start with a group Kitchen between Management and Service, a collection Products, and a stategroup that says whether a product is a basic ingredient or a composed product such as a dish:

'Kitchen': group {
	'Products': collection ['Product'] {
		'Product': text
		'Product type': stategroup (
			'Basic ingredient' { }
			'Composed product' { }
		)
	}
}

For a basic ingredient, the purchase price is registered together with the amount it was bought for — 1000 grams of potatoes for 5 euro, for instance:

'Basic ingredient' {
	'Amount': number positive 'units'
	'Purchase price': number 'thousandth eurocent'
}

That needs a new numerical type, thousandth eurocent:

'thousandth eurocent'
	@numerical-type: (
		label: "Euro"
		decimals: 5
	)

A number such as 500000 is then shown as “Euro 5.00000”. Dividing that by the 1000 grams bought gives “Euro 0.00500” per gram, which stays accurate enough for the calculations that follow.

A composed product consists of other composed products and of basic ingredients. Mashed potato with sauerkraut, for example, consists of the composed product potato mash and the basic ingredient sauerkraut, and potato mash consists of potato, milk and butter. All of them are nodes of the same collection Products:

A composed product should have a cost price: the prices of its ingredients, in proportion to the amounts used. First express that composed products consist of products from the same collection:

'Composed product' {
	'Composed amount': number positive 'units'
	'Ingredients': collection ['Product'] {
		'Product': text -> ^ ^ ^ .'Products'[]
		'Amount': number 'units'
	}
}

Building this model produces an error:

‘property’ Products is a self-reference, but a reference to a sibling is required.

A self-reference is exactly the intention, but the compiler wants it stated as a reference to a sibling. All nodes of one collection are siblings: they live at the same level. Referring from one node to another node of the same collection uses the keyword sibling:

'Composed product' {
	'Composed amount': number positive 'units'
	'Ingredients': collection ['Product'] {
		'Product': text -> ^ ^ sibling
		'Amount': number 'units'
	}
}

Note that this navigation goes up two levels instead of three. A sibling reference points at the level of the collection’s key, not at the level of the collection itself.

Next, the price — first without amounts, to keep the steps small. The group Kitchen so far, including the cost price computations:

'Kitchen': group {
	'Products': collection ['Product'] {
		'Product': text
		'Product type': stategroup (
			'Basic ingredient' {
				'Amount': number positive 'units'
				'Purchase price': number 'thousandth eurocent'
			}
			'Composed product' {
				'Composed amount': number positive 'units'
				'Ingredients': collection ['Product'] {
					'Product': text -> ^ ^ sibling
					'Amount': number 'units'
					'Price': number 'thousandth eurocent' = >'Product'.'Cost price'
				}
			}
		)
		'Cost price': number 'thousandth eurocent' = switch .'Product type' (
			|'Basic ingredient' as $'basic' => $'basic'.'Purchase price'
			|'Composed product' as $'composed' => sum $'composed'.'Ingredients'* .'Price'
		)
	}
}

The price of an ingredient is the cost price of the product it refers to, so 'Price' ... = ... >'Product' .'Cost price' points here:

'Products': collection ['Product'] {
	'Product': text
	'Product type': stategroup (
		'Basic ingredient' {
			...
		}
		'Composed product' {
			...
			'Ingredients': collection ['Product'] {
				...
				'Price': number 'thousandth eurocent' = >'Product'.'Cost price'	// ----> !!
			}
		}
	)
	'Cost price': number 'thousandth eurocent' = switch .'Product type' (	// <---- !!
		...
	)
}

And the cost price of a product depends on its state: a basic ingredient uses its Purchase price, a composed product sums the prices of its ingredients with sum $'composed' .'Ingredients'* .'Price'.

For mashed potato with sauerkraut, the structure of that computation is:

which comes down to:

mashed potato with sauerkraut = €sauerkraut + €potato mash = €sauerkraut + ( €potato + €milk + €butter )

Reading a value from a sibling in a derivation, however, is not something the compiler accepts as it stands. Building the model produces a second error:

cyclic dependency detected for inference ‘dependencies’

pointing at Cost price in this line:

'Price': number 'thousandth eurocent' = >'Product'.'Cost price'

The keyword sibling in 'Product': text -> ^ ^ sibling solved one problem — a user can now say that potato mash consists of potato, milk and butter — and created another: nothing stops a user from saying that potato mash consists of potato, and potato of potato mash, or even that potato mash consists of potato mash. Computing the price of potato mash would then never finish.

Computations that use their own result are called recursive computations. They are useful, but only when something guarantees that they end. The Alan platform provides that guarantee with graph constraints: constraints on the relations (edges) between the nodes of a collection.

The platform has two of them. An acyclic-graph constraint forbids cycles: a node may link to other nodes, but no chain of links may lead back to where it started. An ordered-graph constraint is stricter and puts all nodes in a single chain, from a first node (source) to a last one (sink) — the way the months of a year follow one another.

These constraints restrict the relations between nodes, not their content. Neither of them prevents a user from entering nonsense such as “potatoes consist of eggs and grapefruit” in an acyclic graph, or “the year starts in October and ends in April” in an ordered one. A computer has no concept of potatoes, grapefruit or chicken soup; it only knows which node points at which.

The Products collection needs an acyclic-graph constraint:

'Kitchen': group {
	'Products': collection ['Product']
		'Assembly': acyclic-graph
	{
		...
	}
}

The sibling reference has to become part of that graph, Assembly:

'Product': text -> ^ ^ sibling in ( 'Assembly' )

The graph Assembly records the edges between the products and keeps them acyclic. In the app, a user can no longer select a product that would close a cycle: the app either leaves it out of the list or refuses to save.

Finally, the derivations have to state which graph they follow, so that the platform knows the computation terminates. The keyword recurse does that:

'Price': number 'thousandth eurocent' = ( recurse ^ ^ 'Assembly' ) >'Product'.'Cost price'

and:

'Cost price': number 'thousandth eurocent' = ( recurse 'Assembly' ) switch .'Product type' (
	|'Basic ingredient' as $'basic' => $'basic'.'Purchase price'
	|'Composed product' as $'composed' => sum $'composed'.'Ingredients'* .'Price'
)

The app now shows the products: Kitchen Products

Open Potato mash: Potato mash It consists of butter, milk and potato, each of them a product in the same collection. The price is not right yet: it ignores the amounts.

The price of an ingredient should be the price of the amount used in the composed product. Change the computation of Price and add a Price per unit in the collection Ingredients:

'Ingredients': collection ['Product'] {
	'Product': text -> ^ ^ sibling in ( 'Assembly' )
	'Amount': number 'units'
	'Price per unit': number 'thousandth eurocent' = ( recurse ^ ^ 'Assembly' ) division (
		>'Product'.'Cost price' as 'thousandth eurocent',
		>'Product'.'Amount'
	)
	'Price': number 'thousandth eurocent' = ( recurse ^ ^ 'Assembly' ) product (
		.'Price per unit' as 'thousandth eurocent',
		.'Amount'
	)
}

Price per unit refers to the Amount of a product, which still has to be added. Its derivation has the same shape as the one for Cost price:

'Amount': number positive 'units' = ( recurse 'Assembly' ) switch .'Product type' (
	|'Basic ingredient' as $'basic' => $'basic'.'Amount'
	|'Composed product' as $'composed' => $'composed'.'Composed amount'
)

Price per unit is the cost price of a product divided by the amount it was bought for. Price is that price per unit multiplied by the amount the recipe uses. Cost price stays what it was: the Purchase price for basic ingredients, the sum of the ingredient prices for composed products.

Amounts all use the numerical type units, which can stand for grams, litres, pieces, and so on. Deriving the amount of a composed product from its ingredients would require the volume or mass of every ingredient; that is beyond this tutorial, so a user enters the amount of a composed product.

The numerical type thousandth eurocent needs a product and a division conversion rule, in that order:

'thousandth eurocent'
	= 'thousandth eurocent' * 'units'
	= 'thousandth eurocent' / 'units'
	@numerical-type: (
		label: "Euro"
		decimals: 5
	)

Potato mash is cheaper now, because its ingredients are counted per unit: Potato mash price

One more addition closes the circle between the kitchen and the menu. A stategroup To be put on menu in Products records which products a customer can order:

'To be put on menu': stategroup (
	'Yes' { }
	'No' { }
)

The key Item name of the Menu can then refer to Products, restricted to products whose To be put on menu is Yes:

'Item name': text -> ^ ^ .'Kitchen'.'Products'[] as $
	where 'menu item' -> $ .'To be put on menu'?'Yes'

(NOTE: as $ is implicit after the reference constraint in the first line.)

The definition of Item name is extended with a reference to Products and a where rule that admits only products in state Yes. Adding an item to the menu now means selecting one from that list: Menu items

<tutorial folder: ./_docs/tutorials/restaurant1/2026.2/step_10/>

The End

The restaurant app now covers the whole story: a menu built from the products of the kitchen, orders that are placed, prepared, served and paid, prices and taxes computed from the data itself, and a kitchen that assembles products from other products without ever running in circles.

Three ideas carried all of it:

Where to go next:

Then build something of your own: begin with the end in mind, and experiment. The forum is the place for questions about the language, the platform, or a model you are working on.