release notes

model release notes version partitions-108.6

Model 109

Changes

application language

// new ‘Product’: text -> ^ .’Products’[] as $ where ‘Ordered’ -> ^ .’Ordered Products’>[ $ ]


- Aggregates take their argument between parentheses: `count` takes one plural path, `sum`, `min`, `max` and `std` take an expression list. upgrade/108 converts this:
```js
// old
'Parts Cost': number 'euro' = sum .'Parts'* .'Part Price'
'# Products': number 'items' = count .'Products'*

// new
'Parts Cost': number 'euro' = sum ( .'Parts'* .'Part Price' )
'# Products': number 'items' = count ( .'Products'* )

// new ‘Previous Year’: text -> ^ siblings in ‘Ordered Years’[] ‘Self or Sibling’: text -> ^ siblings || self [] ‘Other Year’: text -> siblings [] ‘Next Years’: reference-set -> siblings in inverse ‘Ordered Years’* .’First’?’No’ = inverse >’Previous Year’ ‘Weeks of other years’: reference-set -> siblings * .’Weeks’* = inverse >’Linked Year’ ‘Later Years’: collection [‘Year’] = union ( ‘this’ = <’Next Years’[ siblings in inverse ‘Ordered Years’* ] ) { … }


- In derivations, one `switch` handles node paths and sets. Its path is a plural path. `| none` is required when the path can produce no node, and `| nodes` when it can produce several; `| node` may be left out when `| nodes` is present. A case that can never match is an error: `| none` on a path that always produces a node, such as a mandatory reference, and `| nodes` on a path that produces at most one node. The set that `| nodes as $` binds can be iterated. A `switch` in a command or action keeps both its cases:
```js
'Open Orders': stategroup = switch .'Orders'* .'Status'?'Open' (
	| nodes as $ => 'Several' ( '# Orders' = count ( $ * ) )
	| node  => 'One' ( )
	| none  => 'None' ( )
) ( ... )

Without | nodes, this switch reports no valid 'upper bound cardinality for set' found for 'match nodes' option 'no'; without | none, no valid 'lower bound cardinality for set' found for 'match none' option 'no'. The old switch on a node path in a derivation always had a | none case. upgrade/108 drops that case from an existence switch on a path that always produces a node. A switch ... is ( ... ) keeps its | none case, which is valid there, and a switch on a set keeps every case it had:

// old
'Price': number 'cents' = switch >'Product' (
	| none => 0
	| node as $ => $ .'Price'
)

// new
'Price': number 'cents' = switch >'Product' ( | node  as $ => $ .'Price' )

// new ‘’ = .’Orders’* as $ on $ >’Debtor’ // group: one entry per debtor ‘this’ = <’Shifts’[ >’Work Center’.’Shifts’* ] // merge: one entry per node the path produces


- A containment `where` rule finds one entry of a collection. In a plural path, a reference step after the rule is accepted only when distinct entries of the iterated collection find distinct entries: the lookup value is unique per entry (its key, or a reference that is its key), or the collection looked up lives inside the entry itself. A grouping path of a union branch may continue over the rule under the same condition. The rules below sit on an order; 'Invoices' and 'Contracts' are keyed by references to 'Order Numbers' and 'Suppliers' and hold a reference 'Debtor':
```js
'Order': text -> ^ .'Order Numbers'[] as $
	where 'Invoice' -> ^ .'Invoices'>[ $ ] // unique: 'Order' is the key
'Supplier': text -> ^ .'Suppliers'[] as $
	where 'Contract' -> ^ .'Contracts'>[ $ ] // repeats: two orders can find the same contract
	where 'Line' -> .'Lines'>[ $ ] // repeats, but every order looks up in its own lines
'# Invoice debtors': number 'items' = count ( .'Orders'* .'Order'&'Invoice'>'Debtor' ) // accepted
// ERROR: no valid 'reference navigation support' found for 'type' option 'dependency'
'# Contract debtors': number 'items' = count ( .'Orders'* .'Supplier'&'Contract'>'Debtor' )
'# Line debtors': number 'items' = count ( .'Orders'* .'Supplier'&'Line'>'Debtor' ) // accepted

‘Lines’: collection [‘Line’] = flatten ( ‘open’ = .’Orders’* .’Status’?’Open’ as $ ( ‘Debtor’ = $ ^ ^ >’Debtor’ ) ) join - { ‘Line’: text = key ‘Debtor’: text -> ^ .’Debtors’[] = parameter }


- The parameters of a command or action are an entity of their own, so a parent step after a state step may not leave them, just as it may not leave a collection entry. A `where` rule that tests a parameter state and then steps out of the parameters fails with `equality constraint violation for 'context entity'`; the upgrade leaves it as it is. Fix it by hand: split the rule into a rule that tests the state and a rule that steps out of the parameters. Where the rule selects collection candidates, take one as the source and add the other with `where`, which keeps the "and". 'Lines' below sits on the item that holds the command:
```js
'Bundle': command {
	'Selection': stategroup ( ... )
	'Picks': collection ['Line'] @default: candidates &'Open' where 'Selected' {
		'Line': text -> ^ ^ .'Lines'[]
			where 'Old' ~> ^ .'Selection'?'All' ^ ^ .'Lines'[] .'Done'?'No' // ERROR: equality constraint violation for 'context entity'
			where 'Selected' ~> ^ .'Selection'?'All' // accepted
			where 'Open' ~> ^ ^ .'Lines'[] .'Done'?'No' // accepted
	}
} => update .'Item' = "bundled"

‘Previous Base’: text -> ^ siblings in ‘Order Copy’[] ‘Previous Copy’: text -> ^ siblings in ‘Order Copy’[] = >’Previous Base’


- The key property of a derived collection requires `= key`. A `flatten` builds its key by joining the branch name and the keys of the entries the branch walks with the separator. Model 108 and partitions-108.1 accepted `= parameter` on the key property of a `flatten`, with an argument for the key in each branch initializer. That argument never set the key, so the keys stay the same. upgrade/108 and upgrade/partitions-108.1 write `= key` and remove the argument:
```js
// old
'Lines': collection ['Line'] = flatten (
	'Order' = .'Orders'* as $'o' (
		'Line' = $'o'.'Number'
		'Amount' = $'o'.'Amount'
	)
) join - {
	'Line': text = parameter
	'Amount': number 'euro' = parameter
}

// new
'Lines': collection ['Line'] = flatten (
	'Order' = .'Orders'* as $'o' ( 'Amount' = $'o'.'Amount' )
) join - {
	'Line': text = key
	'Amount': number 'euro' = parameter
}

// new ‘No’ has-todo: user @description: “Solve this problem in its module.” { }

‘Open Problems’: collection [‘Problem’] = .’Problems’* .’Handled’?’No’ { ‘Problem’: text -> ^ .’Problems’[] .’Handled’?’No’ = key ‘Reported’: stategroup = ‘No’ ( ) ( ‘No’ has-todo: user @description: “Report this problem.” { } ‘Yes’ { } ) }

A todo on a root, group or collection node is moved by upgrade/108 to the one state of a new derived stategroup `'Todo item'`, the node's last property, hidden from the views. Its requirement gets a leading `^` on every path that started at the node, because it now starts at the state's node:
```js
// old
'Problems': collection ['Problem'] {
	has-todo: user where ( .'Status'?'Open' ) @description: "Solve this problem in its module."
	'Problem': text
	'Status': stategroup ( ... )
}

// new
'Problems': collection ['Problem'] {
	'Problem': text
	'Status': stategroup ( ... )
	'Todo item': stategroup = 'Yes' ( ) @hidden (
		'Yes'
			has-todo: user where ( ^ .'Status'?'Open' ) @description: "Solve this problem in its module."
		{ }
	)
}

The requirement still controls when the todo applies. Replace the generated stategroup by one of your own when you want an explicit state, as ‘Handled’ above. A todo on the parameters of a command or action has no derived stategroup to carry it and is dropped. A node that already has a property 'Todo item' cannot take the new one: rename that property before you upgrade.

After the upgrade

./alan upgrade runs upgrade/108 on every model 108 (platform before-partitions.1) and migration source model. The transformation converts what it can, and the build that follows reports what is left: that is the work by hand. These are the errors it reports for the changes above, each with its cause and the change that shows the fix.

Errors that converted code can report:

Errors in code that was not converted, such as a model upgraded by hand: