Application Tutorial:
a Restaurant app
Part II

  1. Introduction
  2. Take-away
  3. Derived values
  4. Growing our business
  5. Conditional expressions
  6. Usages and reference sets
  7. Upstream and downstream
  8. Commands and actions
  9. The End

Introduction

This is the second part of the application language tutorial where we build a restaurant app. Make sure that you have completed the first part before continuing.

Let’s recap: in the first part, we wrote a small data model for our restaurant. From that data model, we generated a web application for entering a menu, tables, and orders. In this second part you will learn about derived values, evaluation phases, and commands & actions.

Take-away

You decided that your restaurant will also provide a take-away service. For that, we have to make adjustments to the model. Let’s see what is needed.

Orders will no longer be part of Tables: we can have Orders without Tables. But, for an Order, we do want to know if it is a Takeaway order, or if it is an In-house order for a table. For that, we add an Order type.

So, we rewrite this piece of code:

'Tables': collection ['Table number'] {
	'Table number': text
	'Seatings': number 'chairs'
	'Orders': collection ['Order line'] {
		'Order line': text
		'Item': text -> ^ ^ .'Menu'[]
		'Amount': number 'units'
	}
}

to this:

'Tables': collection ['Table number'] {
	'Table number': text
	'Seatings': number 'chairs'
}

'Orders': collection ['Order'] {
	'Order': text
	'Order type': stategroup (
		'Takeaway' { }
		'In-house' {
			'Table': text -> ^ ^ .'Tables'[]
		}
	)
	'Order lines': collection ['Order line'] {
		'Order line': text
		'Item': text -> ^ ^ .'Menu'[]
		'Amount': number 'units'
	}
}

What changed?

Derived values

Our customers have to pay us, and for that we need to compute the cost of their orders. Let’s start with a Line total: the cost of a single Order line. The Line total of an Order line is a derived value, computed from the (base values) Selling price and the Amount:

'Order lines': collection ['Order line'] {
	'Order line': text
	'Item': text -> ^ ^ .'Menu'[]
	'Amount': number 'units'
	'Line total': number 'eurocent' = product (
		>'Item'.'Selling price' as 'eurocent' ,
		.'Amount'
	)
}

After eurocent for the Line total we have have written a formula for deriving the Line total of an Order line. The formula (derivation expression) expresses that the Line total is computed by multiplying (taking the product of) the Selling price and Amount.

For retrieving the Selling price of the Item we use the notation >'Item'. That means, starting at the Order lines node, follow the Item reference (expressed by -> ^ ^ .'Menu'[]). So, at runtime, >'Item' gives us the Menu item that corresponds to an Order line.

After Selling price, there is some special code: as 'eurocent'. To explain what this means and for the health of our model we need to add this line: = 'eurocent' * 'units' below euro so our numerical-types section now looks like this:

numerical-types
	'eurocent'
		= 'eurocent' * 'units'
		@numerical-type: (
			label: "Euro"
			decimals: 2
		)
	'chairs'
	'units'

The as statement is followed by what is called a product conversion rule. Such a rule describes how numerical-types of the two properties are multiplied, like euro and unit. So the general format of a product conversion rule is = 'num-type1' * 'num-type2'. The first part (num-type1) is wat we mention after the keyword as. NOTE: for future versions of Alan, we plan to let the compiler infer this.

For a division, we see something similar: = division ( 'property1' as 'division conversion rule' , 'property2' ) The division uses a division conversion rule: = 'num-type1' / 'num-type2'

And we can do conversion of units by a constant factor, using the from statement, for example: 'Circle circumference': number 'circum' = from 'diameter' .'Circle diameter' And a singular conversion rule: = 'num-typ' * constant, according to the example:

numerical-types
	'circum'
		= 'diameter' * 314159265 * 10 ^ -8	// = diameter * pi

Conversion rules ensure that numerical-types for derived values are correct. They also let you reuse (singular) conversions.


Let’s add another useful line to the model:

'Total': number 'eurocent' = sum .'Order lines'* .'Line total'

This line expresses that Total is the sum of the Line total from a set of Order lines. For that purpose, the expression starts with the keyword sum, followed by a path that produces a set of numbers to sum. The grammar calls this an object set path. The object set path starts with navigation steps that lead to a collection: .'Order lines'. Subsequently, the keyword * expresses expansion of the collection to a set of objects: Order line nodes. The expression concludes with a value path (.'Line total') expressing which value from each Order line should be retrieved.

Can you determine where the line needs to go? Spoiler alert: answer ahead…

So now our whole model looks like this:

users
	anonymous

interfaces

root {
	'Beverages types': collection ['Beverage type'] {
		'Beverage type': text
	}

	'Menu': collection ['Item name'] {
		'Item name': text
		'Selling price': number 'eurocent'
		'Item type': stategroup (
			'Dish' {
				'Dish type': stategroup (
					'Appetizer' { }
					'Main course' { }
					'Dessert' { }
				)
			}
			'Beverage' {
				'Beverage type': text -> ^ ^ .'Beverages types'[]
			}
		)
	}

	'Tables': collection ['Table number'] {
		'Table number': text
		'Seatings': number 'chairs'
	}

	'Orders': collection ['Order'] {
		'Order': text
		'Order type': stategroup (
			'Takeaway' { }
			'In-house' {
				'Table': text -> ^ ^ .'Tables'[]
			}
		)
		'Order lines': collection ['Order line'] {
			'Order line': text
			'Item': text -> ^ ^ .'Menu'[]
			'Amount': number 'units'
			'Line total': number 'eurocent' = product (
				>'Item'.'Selling price' as 'eurocent' ,
				.'Amount'
			)
		}
		'Total': number 'eurocent' = sum .'Order lines'* .'Line total' // <-- ;-)
	}
}

numerical-types
	'eurocent'
		= 'eurocent' * 'units'
		@numerical-type: (
			label: "Euro"
			decimals: 2
		)
	'chairs'
	'units'

And at runtime (in your app) it looks like this: (line totals and total

Note that operations such as sum work on both a set of values as well as on a list of values. For summing a list of separate values, you use parentheses after the sum keyword, as we did for product:

sum ( .'value A', .'value B', .'value C', .'value D' )

The supported operations for deriving number values can be found here.

Examples of absolute and relative numbers:

So far we’ve seen number derivations, but we can also derive other types of data.

<tutorial folder: ./_docs/tutorials/restaurant1/step_04a/>

Growing our business

Our restaurant business is growing. Many people are now working with our app, and its time for some reorganization. We have more permanent data that only Management should touch, and we want to show that in our application. For that, we express a group called Management at the top of the model. This group captures a new collection Discount periods, a VAT percentage, and the existing collections Beverages types and Tables as shown below.

When rebuilding the app, you’ll get some errors saying the compiler can’t find certain properties. Correct the errors according to the changes made, by adding missing navigation steps such as .'Management'.

'Management': group {
	'Discount periods': collection ['Period'] {
		'Period': text
		'Percentage' : number 'percent'
		'Minimal spendings': number 'eurocent'
	}
	'VAT percentage': number 'percent'
	'Beverages types': collection ['Beverage type'] {
		'Beverage type': text
	}
	'Tables': collection ['Table number'] {
		'Table number': text
		'Seatings': number 'chairs'
	}
}

The VAT percentage will be used for calculating the value added tax (VAT). For the VAT percentage, we also have to add a numerical type to the numerical-types section in the model:

'percent'

The Discount periods are for discounts during different time periods, where the discount depends on the amount of money spent at the restaurant.

Rename the attribute Total for Orders to Subtotal:

'Subtotal': number 'eurocent' = sum .'Order lines'* .'Line total'

The actual Total cost will depend on a discount when applicable.

<tutorial folder: ./_docs/tutorials/restaurant1/step_05/>

Conditional expressions

Now let’s add a stategroup Discount applicable to Orders:

'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'
			)
		)
		'Total': number 'eurocent' = sum ( ^ .'Subtotal' , - .'Discount' )
	}
	'No' { }
)

Now, add the required numerical type fraction, the product conversion rule, and the singular conversion rule to the numerical-types section:

numerical-types
	'eurocent'
		= 'units' * 'eurocent'
		= 'fraction' * 'eurocent'
		@numerical-type: (
			label: "Euro"
			decimals: 2
		)
	'chairs'
	'units'
	'percent'
	'fraction'
		= 'percent' / 1 * 10 ^ 2

With this model extension, you can apply a discount by selecting a Discount period from the previously added collection Discount periods. For example, selecting ‘Summer holiday’ will give 3% discount on spendings over €35. If a discount is applicable (state Yes) and a Discount period is selected, the Discount will be computed after saving your changes:

With the expression, we switch on the comparison (compare) result of two numbers. We can match against the following possible results for the comparison:

Finally, the Total is calculated by subtracting Discount from the Subtotal using the sum operation and a sign inversion (-).

Let’s take a look at the app and enter an order: discount An order with a subtotal of €40,20 receives a discount of 3% when spending €35 or more during the summer holiday.

Now, let’s calculate the tax (VAT). To do this we need to know the Total cost and take a percentage of it. But, the actual Total cost of an Orders item depends on whether a discount was applied or not. For that purpose, we use a switch statement for switching on the state of a stategroup attribute, after which we can compute the VAT from the Total of the order:

'Total': number 'eurocent' = switch .'Discount applicable' (
	|'Yes' as $'discount' => $'discount'.'Total'
	|'No' => .'Subtotal'
)
'VAT': number 'eurocent' = product (
	from 'percent' ^ .'Management' .'VAT percentage' as 'fraction' ,
	.'Total'
)

For computing the Total, we switch on the state of Discount applicable. In case of state Yes we use a special instruction as $'discount'. This stores the Yes node under the name $'discount'. Because of that, we can refer to it in the remaining part of the expression (and only there): $'discount'.'Total'. We call this a named object (or constant variable).

Here’s the result of our work: VAT

For each property type that the application language supports, the language also supports expressing for deriving such values. So, you can derive text values, file values, references, stategroup states, and even collections. You can find many examples in the application language documentation.

<tutorial folder: ./_docs/tutorials/restaurant1/step_06/>

Usages and reference sets

References are by default unidirectional. However, it is often useful to ‘invert’ those references: for Tables we may want to know which Orders have been placed for it.

You may have noticed the Usages button in your app, when viewing a Tables item. Clicking on Usages, gives you a screen with exactly that information: which Orders were placed for the Tables item.

Let’s try that with a new order. Select Orders in the left column and click ‘Add’ (note that you can currently not ‘Add’ items from the Usages screen directly):

order

Insert ‘001’ as the order number, choose In-house and select a table. Just for fun, add some order lines as well:

order lines

Now, hit Save! Go to Tables, pick the table you selected when placing the order and click Usages:

usages

We can see that in our version Order ‘001’ is using table ‘T03’:

table used

If we click the order, we jump to the order with its order lines.


In the web app, we have this nice ‘Usages’ screen. The web app computes these screens for us. However, we cannot use these ‘Usages’ in computations. For that, we need bidirectional references.

You can turn unidirectional references into bidirectional references with a reference set. A reference set holds inverse references, which are identical to the usages that we just saw. Let’s add that reference set, and some derivations that use it:

'Tables': collection ['Table number'] {
	'Table number': text
	'Seatings': number 'chairs'
	'Orders': reference-set -> downstream ^ ^ .'Orders'* .'Order type'?'In-house' = inverse >'Table'
	'Number of orders': number 'units' = count <'Orders'*
	'Total order value': number 'eurocent' = downstream sum <'Orders'* ^ .'Total'
}

Also, add -<'Orders' at the Table reference below 'In-house' in your model.

'Table': text -> ^ ^ ^ .'Management' .'Tables'[] -<'Orders'

Now, build it and take a look at the app. For each Tables node we can now see how many Orders have been placed at that table. Furthermore, we can see the Total order value for the Orders placed at a specific table. Nice stats that may enable us to optimize the placement of our Tables.

For expressing the reference set, we use a special keyword downstream followed by a navigation path. The keyword downstream indicates in which computation phase the reference set is available. You can read more about that in the next section.

The navigation path contains the keyword * instead of [] that we saw for unidirectional references. That is because multiple Orders can reference the same table; the reference-set for a specific table will hold all Orders that refer to that specific table. Finally we say: take the inverse of the Table reference that you find under In-house.

For Table, we expressed that for the reference, its inverse (-< instead of ->) should be stored in the reference set Orders of the Tables item.

<tutorial folder: ./_docs/tutorials/restaurant1/step_06a/>

Upstream and downstream

Computations for Alan applications are divided over multiple different phases to guarantee that derivations can always be evaluated. Alan distinguishes between two different types of computations: constraints and derivations.

The application language supports a single class of constraints: mandatory references. The runtime ensures that derivations can always be evaluated when constraints are checked. That means that every derivation will always produce a valid value as specified in your model (and never an undefined/null-value).

The Alan runtime evaluates computations in several different phases. You can find a detailed explanation in the documentation. In short, it boils down to this:

  1. The runtime checks upstream constraints by traversing your model in a top-to-bottom order.
  2. The runtime checks downstream constraints by traversing your model in a compiler-optimized order.
  3. The runtime computes upstream derivations by traversing your model in a top-to-bottom order.
  4. The runtime computes downstream derivations by traversing your model in a compiler-optimized order.

Upstream is the default phase for constraints and derivations; upstream computations do not require an annotation. You can think of the compiler-optimized order for downstream computations as the opposite of upstream: from bottom-to-top. This often applies, as expressions have that immediate implication. For example, take this Table reference:

'Table': text -> ^ ^ ^ .'Management' .'Tables'[]

The Management group has been specified before Table in the model, as the code expresses an upstream reference. Because of that, the expression for the reference set Orders must be a downstream reference (opposite direction in the model). Therefore, the language requires the downstream annotation.

Computations can depend on earlier computed values. Thus, values that are computed in an earlier phase, or values that are computed earlier according to the model-traversal order. For example, the Table reference is computed in phase 1. Therefore, references in a reference-set of Orders are available in phase 2. The 'Number of orders' is an upstream derivation evaluated in phase 3: it can depend on the reference set.

Finally, 'Total order value' is a downstream derivation evaluated in phase 4. The derivation depends on the Total of the Orders, which we compute in phase 3. Notice that you cannot remove the keyword downstream there. If you do so, you move the computation to phase 3: the upstream derivations phase. But, for the Orders, the Total will not yet be computed, as upstream means traversal from top to bottom.

Commands and actions

When we build Alan applications, we often connect them to other systems that send information. For example, suppose that we need to connect our restaurant application to a third party app for placing orders. We then typically define an Alan interface (which looks a lot like an application), expressing which commands the third party app can send. For example, a command Place new order for placing a new order.

Our app needs to implement the Place new order command for it to work. We do that with application language.

NOTE: discussing the Alan interface language goes beyond the scope of this tutorial. If you would like to see a tutorial about the Alan interface language, and interconnecting multiple different applications, please let us know on the forum.

Alternatively, we need a user interfaces that works well on a mobile device with clickable buttons to execute operations. In addition, users may need to perform multiple different operations on data in sequential manner to complete a task. For that purpose, Alan supports action’s.

Commands and actions are nearly identical with regard to how you express them. But, commands are executed on the server, whereas actions are executed on the client (by your web application). Which of the two you should choose depends entirely on your use case, as illustrated above.

Now let’s look at our implementation of Place new order for the third party app:

'Place new order': command {
	'Provide an order number': text
	'Where is the meal consumed?': stategroup (
		'Outside of restaurant' { }
		'At the restaurant' {
			'Where is the customer seated?': text -> .'Management'.'Tables'[]
		}
	)
	'Order lines': collection ['Provide an order line number'] {
		'Provide an order line number': text
		'Item to be consumed': text -> .'Menu'[]
		'Amount of this item': number 'units'
	}
} => update .'Orders' = create (
	'Order' = @ .'Provide an order number'
	'Order type' = switch @ .'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 @ .'Order lines' as $ (
		create (
			'Order line' = $ .'Provide an order line number'
			'Item' = $ .'Item to be consumed'
			'Amount' = $ .'Amount of this item'
		)
	)
	'Discount applicable' = create 'No' ( )
)

That’s a lot to take in! Let’s take a look at the result in the webbrowser. Add the code to your model at the bottom, on the root type. Then, build the app and go to your browser. In the left column a button ‘Place new order…’ appears:

command

The title of this button corresponds to the label of our command: Place new order Click it and fill in the fields, for example like this:

form filled

Now click Place new order below the form and then click Close (top right). Then go to Orders and select your order:

added order

We have successfully added an order to the Orders collection by filling out the requested fields in the form. If you closely inspect the example, you can see that the choice for At the restaurant in answer to the question Where is the meal consumed? is translated into the state In-house of stategroup Order type.

Let’s take a closer look at our code for the command. The first part of the command looks a lot like a data model. And that is what it is: for expressing the required parameters (fields) for a command, we define a small data model:

'Place new order': command {
	'Provide an order number': text
	'Where is the meal consumed?': stategroup (
		'Outside of restaurant' { }
		'At the restaurant' {
			'Where is the customer seated?': text -> .'Tables'[]
		}
	)
	'Order lines': collection ['Provide an order line number'] {
		'Provide an order line number': text
		'Item to be consumed': text -> .'Menu'[]
		'Amount of this item': number 'units'
	}
} ...

In this small data model inside our main model, we can express references to the main model, like -> .'Tables'[]. Like for references in our main model, the navigation expressions are evaluated with respect to the node where we call the command. So, for a command on the root, the navigation expression is evaluated with respect to the root node. If instead we have a command called Remove order line for Orders, we will get a button when opening an Orders item. When picking a Line from the Order lines, we can choose from the lines from the open Orders item. Navigation expressions are evaluated with respect to the Orders item for which we click the command button, called the ‘this’-node.

'Orders': collection ['Order'] {
	'Order': text
	'Remove order line': command {
		'Line': text -> .'Order lines'[]
	} => update .'Order lines' = delete @ >'Line'
}

If needed, you can also express references inside the small data model by starting the reference with the special keyword @. We use this special keyword for jumping to the nearest parameter node at runtime.

After the mini model that expresses required parameters for the command, we express the operations that are to be performed, like:

... => update .'Orders' ...

The double arrow can be read as ‘do’. The keyword update expresses that we want to update an attribute. The path that follows resolves to the attribute value that we want to update: the collection of Orders.

Instead of update we can also use these keywords: switch, ignore, walk, execute, and external, just so you know that there are multiple options. Let’s stick with update for now, as we want to update the specified collection with provided data.

The command implementation concludes with:

		... = create ( ... )

This tells us what the update should be about: ‘the update is (=) the creation (create) of a node. Instead of create, you can also use ensure. Create will check if an entry in the target collection with the provided key already exists, and reject the whole command in that case. The ensure operation does not fail. It creates the node, or otherwise overwrites values in the way that you specify in your model.

The part between the parentheses of keyword create reads:

'Order' = @ .'Provide an order number'
'Order type' = switch @ .'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 @ .'Order lines' as $ (
	create (
		'Order line' = $ .'Provide an order line number'
		'Item' = $ .'Item to be consumed'
		'Amount' = $ .'Amount of this item'
	)
)

This part expresses how the new order is constructed from the command parameters. For example, the attribute Order will hold whatever the external app provides for Provide an order number. Note that we use the keyword @ here multiple times to jump to the nearest parameter node (the dataset that conforms to our mini model).

<tutorial folder: ./_docs/tutorials/restaurant1/step_06b/>

The End

This concludes the tutorial into the application language, at least for now: there is more to follow soon. For a sneak preview, you can take a look at the additional tutorial folders we provided, and see if you can understand the models yourself. The documentation can also help you a lot there, and don’t forget about the forum!

Have fun building your own applications!