Rules: properties
Property rules
-
The assistant MUST NOT declare a property if it is used only once.
Exception: a property may still be declared if it is added to a form.
-
Every property parameter MUST be used in its expression. Unused parameters are forbidden.
-
The assistant MUST assume standard
NULLpropagation for property expressions: if any parameter isNULL, the result isNULL.Exceptions that do NOT nullify on a single
NULLoperand: the selection operators —OVERRIDE, which returns the first non-NULLoperand, andIF ... THEN ... ELSE, whose CONDITION is theNULL-tolerant part: aNULLcondition takes theELSEbranch, while a non-NULLone returns theTHENvalue as it is, soIF TRUE THEN NULL ELSE 1isNULL—MIN/MAX, theNULL-tolerant arithmetic(+)/(-), theCONCATconcatenation (aNULLoperand is skipped together with its separator), andGROUPaggregates (GROUP SUM,GROUP MAX, etc.) — aNULLoperand or value is skipped instead of propagating.OR,NOTandXORdo not propagate either: they read a non-NULLoperand asTRUE, soNULL OR TRUEisTRUEandNOT NULLisTRUE.ANDis the one that does — it returnsTRUEonly when both operands are non-NULL, soTRUE AND NULLisNULL.GROUP LASTskips aNULLonly while it has noWHERE, where non-NULLness of the aggregated expression is what serves as the condition; given an explicitWHERE, a row satisfying it contributes its value even when that value isNULL.These exceptions still yield
NULLwhen:- every operand or aggregated value is
NULL— exceptNOT, whose whole point is to answerTRUEthere; (+)/(-)orGROUP SUMproduces0(a zero result is returned asNULL).
- every operand or aggregated value is
-
The assistant MUST NOT use
GROUP AGGRinside arbitrary expressions.GROUP AGGRis allowed only in property definitions.When reasoning about it, the assistant MUST treat
GROUP AGGRasGROUP MAXwith an additional constraint. -
The assistant SHOULD avoid unnecessary conditions when the language semantics already produce the required result.
-
The assistant MUST NOT create a property whose expression is equal to one of its parameters.
-
The assistant MUST NOT create multiple properties with identical expressions.
-
If a property is calculated from another property but has different parameters, the assistant SHOULD try to keep the same property name.
-
To check whether a property is
NULL, the assistant SHOULD useIF NOT property(...).To check that it is not
NULL, the assistant SHOULD useIF property(...). -
The assistant SHOULD specify
CHARWIDTHin the property definition rather than in form design.
For a simple property composition that only forwards
another property, the assistant SHOULD NOT repeat
CHARWIDTH on the derived property unless it must differ.
-
For static objects, the assistant MUST NOT use
staticCaptionorstaticNameproperties.The assistant MUST use
captionandnameinstead.This applies to writing as well:
captionandnameare simple compositions over the stored caption and name, so an assignment to them passes into the stored property. A static object's caption is assigned throughcaption; the name must not be changed — changingnameis forbidden by a system constraint. -
Property names SHOULD be concise and avoid unnecessary words.
-
The assistant SHOULD NOT use words in a property name that duplicate parameter class names unless required for clarity.
-
The assistant SHOULD NOT specify an explicit namespace for a property unless necessary.
-
When creating a DATA property — or a simple composition over a DATA property (for example, pulling the name of a related object) — for a single object's own attribute, the assistant MUST deliberately decide whether to place it in the system
idorbasegroup viaIN.Attributes that form the object's business identity and appear in its representation SHOULD go in the
idgroup; other primary attributes go in thebasegroup (idis nested underbase).A property SHOULD NOT be placed in
idorbasewhen it is not the object's own primary attribute. -
When dividing values of integer classes, the assistant MUST cast one of the operands to
NUMERIC, not the result.The ratio of two integers is integer division, so an outer cast like
NUMERIC[16,4](a * b / c)silently drops the fractional part; the correct form isNUMERIC[16,4](a) * b / c. -
A parameter's class annotation (
prop(SubClass x)) is a signature, not a runtime filter: it resolves same-named properties and sets the signature, but the computed set is determined by the properties used in the expression. Reading a parent-class property with a subclass-annotated parameter still ranges over ALL objects of the parent class (e.g. in aGROUP SUM— silently wrong totals).To restrict the set to a class, the assistant MUST add an explicit
x IS SubClasscondition (or use a property declared on that subclass). -
In the
GROUP ... BYoperator the assistant MUST NOT list in theBYblock the upper parameters used in the operator's expressions: each such parameter is already implicitly a group — a parameter of the created property — and keeps its place in the signature.With an explicit parameter list on the left, the
BYexpressions are mapped in order only to the parameters not used in the expressions; a mismatch in count or classes is an error.
Abstract property rules (+=)
-
The value class of a
+=implementation MUST fit within the value class declared on the abstract property; there is no implicit cast — an implementation with a wider class is rejected at server startup with a "wrong value class of implementation" error.An expression that widens the value class — above all string concatenation, which sums the operands' lengths (
ISTRING[326]against a declaredISTRING[250]) — the assistant MUST wrap in an explicit cast to the declared class:f(X x) += ISTRING[250](a(x) + b(x));
Ordering rules (ORDER)
-
Where two rows can share an order key and the answer depends on which of them wins — which of two same-date rows is the
GROUP LAST, which of two equal-priority rows aTOP 1takes, where aPARTITION PREVsteps back to — the assistant MUST spell the tiebreak out, usually as the object itself:ORDER date(d), d.The platform does fill an incomplete order in on its own for several of these, so the symptom is not randomness between runs; it is that the row chosen is whichever one a service order over the interfaces selects, which is not what the domain asked for. Writing the tiebreak is how the choice becomes the intended one.
-
A cumulative
PARTITION SUM ... ORDERwith noTOPorOFFSETis the case where a tiebreak MUST NOT be added by reflex. Its default frame gives every row sharing an order key the same cumulative value. Adding a tiebreak changes the result — from a total per group of equal keys to a total per row — which is a decision about the domain, not a safety measure. UnderTOPorOFFSETrule 1 applies as usual: those pick rows, and which rows they pick is worth saying. -
PARTITION LASTdoes not read the order to compute its value: it is the value of the current row.GROUP LASTis the one that picks by order.