# Narrowing by the constraints of a structure

[Narrowing](../narrowing.md) › structure. The names in the examples are those of [section 2](../narrowing.md#2-the-names-in-the-examples).

A constraint of a model applies to a **subject**: a variable, or a member of it. The member is named by the steps down to it, the key of a component of a context and `:each` for the elements of a list, so `{"q", ["age"]}` is the age of `q` and `{"qs", [:each, "age"]}` the age of each person of `qs`. A constraint is one of two kinds, both unary tests (D-161, step E):

- the **allowed values** apply to the value, and for a list to each of its elements (the `allowedValues` of an item definition, D-51);
- the **type constraint** applies to the value as a whole, so for a list it applies to the list and not to the elements (the `typeConstraint`): `count(?) > 0` says that the list has an element.

A subject can have several constraints, and all of them hold. A member that is missing or `null` is exempt, as a variable that is `null` is, and a value with a member that is not allowed is `null` as a whole, with an error that says which member (`the value of "qs[3].age" is not one of its allowed values`). The type of a read of the name reflects all of them, at the member they apply to: they are conditions of `?`, and narrow as the [number](number.md) and the other pages say.

| Expression | Type | Warnings |
| --- | --- | --- |
| `q` | `context<age: number in [0..150] \| Null, name: string \| Null> \| Null` | — |
| `q.age` | `number in [0..150] \| Null` | — |
| `q.age + 1` | `number in [1..151] \| Null` | — |
| `q.age * 2 - 1` | `number in [-1..299] \| Null` | — |
| `q.name` | `string \| Null` | — |
| `q.age > 150` | `boolean = false \| Null` | — |

The elements of a list are narrowed in the same way, whether the list is read as a whole, by an index, by a path over the list (`qs.age`) or by a loop:

| Expression | Type | Warnings |
| --- | --- | --- |
| `qs.age` | `list<number in [0..150] \| Null> \| Null` | — |
| `qs[1].age` | `number in [0..150] \| Null` | — |
| `for e in qs return e.age + 1` | `list<number in [1..151] \| Null> \| Null` | — |
| `sum(qs.age)` | `number \| Null` | — |

A list of lists has `:each` twice for its elements' elements, and a constraint for the inner lists as values is one for `[:each]`. Here `ls` allows `> 0` for the numbers in its lists, and `ws` has the type constraint `count(?) > 0`:

| Expression | Type | Warnings |
| --- | --- | --- |
| `ls` | `list<list<number in (0..max] \| Null> \| Null> \| Null` | — |
| `ls[1][1]` | `number in (0..max] \| Null` | — |
| `ls[1][1] + 1` | `number in [1..max] \| Null` | — |
| `ws` | `non-empty list<number \| Null> \| Null` | — |
| `count(ws)` | `number in [1..max] \| Null` | — |
| `ws[1]` | `number \| Null` | — |
| `count(ws) > 0` | `boolean = true \| Null` | — |

## A constraint that mentions another name

A test reads the names of the scope as the expression does: converted to their declared types and **verified** against their own allowed values (D-170). So `capped`, which allows `<= top`, sees a `top` that is `null` when `top` is not in `[18..65]`, and the type of `top` is a type that the test can rely on. The type of `capped` is cut by the bounds of it: `capped <= top` is true for a number up to the highest possible `top`, and for no other.

| Expression | Type | Warnings |
| --- | --- | --- |
| `top` | `number in [18..65] \| Null` | — |
| `capped` | `number in [-max..65] \| Null` | — |
| `capped + 1` | `number in [-max..66] \| Null` | — |
| `capped < 100` | `boolean = true \| Null` | — |
| `capped > 65` | `boolean = false \| Null` | — |

A member sees the other members of its value by the name of the variable: the age of `emp` allows `<= emp.limit`, and the limit of `emp` is a constraint of its own, which holds for the `emp` that the age sees. The age is cut by it.

| Expression | Type | Warnings |
| --- | --- | --- |
| `emp.limit` | `number in [0..65] \| Null` | — |
| `emp.age` | `number in [-max..65] \| Null` | — |
| `emp.age <= 65` | `boolean = true \| Null` | — |

Only an allowed value is a relation that the type can use, and only through the bounds of the other name: `capped` is not `top` or below for a *given* `top`, it is at most the largest. What the run does, and why the test has to see a verified name:

- **The value that a test sees.** `top` bound to 100 is not allowed, so it is `null` for `capped`, and `capped <= null` is `null`: `capped` is not allowed either, whatever its value is. Were the test to see the 100, `capped` = 90 would be allowed, and `top` would be `null` in the expression: `capped` would be a number above 65, outside its type.
- **Where it stops.** The constraint that is being compiled, and the ones whose tests read it, are not applied to the name that is read: `sr` allows `<= sr`, and `ca` and `cb` that allow `<= cb` and `<= ca`, read the value that was bound, as they did, and so does anything that is more than four constraints deep, and every name that the tests of a name mention when they reach more than 64 compiles of other names (a name decides that by what its tests reach, and not by what else the expression reads). The other constraints of the same name are applied (that is how `emp.age` sees the limit).
- **The stances.** `:enforce` and `:enforce_wide` run it, and `:enforce` types it. The stances that leave a value as it is (`:report`, `:audit`, `:trust`, `:ignore`) read every name as it was bound, and `capped` is `number | Null` in them.
- **A name that has no allowed values** is converted to its declared type, and its type is the declared one: `x <= y` as an allowed value of `x` narrows nothing for a `y` that is any number.

## What it does not say

- The tests of a member see its siblings only by the name of the variable (`emp.limit`). A constraint of a named type, which is for every variable of the type, has no name to read the value by: how to refer to the value that a member is a member of is open ([A19], D-170).
- A list that is not empty is not kept inside another list: the parts of a list type are plain (D-152), so `count(?) > 1` for the inner lists does not narrow `count(ls[1])`.
- `Feel.Scope` member-constraint maps attach to scope variables, not arbitrary FEEL locals. Result allowed values have a separate option. DMN can also embed declared-value checks carrying an item definition's constraints inside boxed expressions, including parameters and internal values; those are separate from attaching a scope constraint to a FEEL loop variable.

The decisions: D-138 (allowed values), D-51 (collections), D-161 in [decisions.md](../decisions.md).
