Some macros are actually stands not for a single value but for a set of values. For example $recipient macro – because there may be more then one recipient for a message. Or $body_part macro – because there may be more then one body part in a message. In usual programing language this multiplicity would be expressed with arrays, loops, indexes and related mechanisms. In ESM such mechanisms are not provided to keep syntax as simple as possible and to avoid the need for “low-level” programing. Administrator just writes macros and then implementation does automatic handling of multiplicity “behind the scene”. However at some point Administrator may need to know how it works to do some things right and to modify implicit behaviour using simplified multiplicity handling tools. The way ESM handles multiplicity is described in this article.
Implicit iteration is a general idea of how multiplicity is handled. In the root of implicit iteration is the idea that if a macro may have multiple values and is used in a template then corresponding template must be “repeated” for each value of a macro. Term “repeated” is quoted because it needs further refinement depending on a context where a template is used. If a template is used in an option for an engine action then this action (i. e. query to DB, saving file or whatever an engine does) must be repeated for each value of a template calculated using each value of a macro. Because engine result logically depends on action then it also gets multiple values each corresponding to each performed action. If a result additionally depends on another multi-value macro than iteration through values of this macro is additionally performed and thus number of result values gets multiplied. For a precondition Administrator may specify whether all or any iteration of a precondition have to be met for action to be executed and corresponding results to be calculated.
More precise and formalized description of implicit iteration is provided with the help of matrix and dimension notions. Each multi-value macro internally is presented as matrix. Matrix is associated with one or more named sets of indexes. This sets are called dimensions. dimensions are just sets of integer values in range from zero to some max value. Macro value is calculated as matrix value by selecting one index from each associated dimension and mapping of obtained combination of indexes into associated matrix value. Iteration through all possible macro values is done by iteration through all possible combination of indexes and mapping them to matrix values. Association of a macro with dimensions is depicted in the following form:
$recipient => ($recipient) $body_part => ($body_part) $engines.db.search.id => ($recipient, $body_part)
Left hand of an expression is a macro name (with “$” at the beginning) then “=>” operator then in parenthesis comma-separated list of dimension names (with “$” at the beginning). Order of dimensions in a list is important if macros that manipulate dimensions (such as $wrap) are going to be used. Otherwise it is not important. Most multi-value common macros has single dimension with same name as macro name.
Union is an operation with two sets of dimensions which has as result another set of dimensions that contains all dimensions from both initial sets. Union is depicted using "U" character between two sets of dimensions. Algorithm is the following:
- Ordered lists of first and second sets of dimensions are concatenated.
- Then starting from right to left duplicated Dimensions are removed.
($recipient) U ($body_part, $recipient) = ($recipient, $body_part)
Subtraction is an operation with two sets of dimensions which has as result another set of dimensions that contain those dimensions that are contained in first set but not in second. Subtraction is depicted using "-" character between two sets of dimensions. Algorithm is the following:
- Take dimensions from first set one-by-one from left to right and check if same dimension is contained in second set.
- If it is contained then do nothing.
- If it is not contained then add this dimension to the end of result set.
($body_part, $recipient) - ($recipient) = ($body_part) ($recipient) - ($body_part, $recipient) = ()
Precondition, action and result dimensions
Precondition dimensions are a union of dimensions of all used macros taken as they appear from left to right and from top to bottom in XML. Action dimensions are a union of dimensions of all used macros in all action options taken as they appear from left to right and from top to bottom in XML. Result dimensions are a union of dimensions of all used macros in all result options taken as the appear from left to right and from top to bottom in XML.
Main iteration algorithm
Implicit iteration within an engine is performed by the following algorithm:
- Everything starts from iterations by action dimensions.
- At each action dimensions iteration preconditions are checked.
- For each precondition additional iterations is done by additional dimensions that is a subtraction of action from precondition dimensions. Precondition is considered to be matched at additional iteration if it has value and its value is true.
- Final value of a precondition is calculated from values obtained at additional iterations. The way it is done depends on value of match attribute for precondition:
- "any" – (default) if any is matched then final value is matched; otherwise isn't;
- "all" – if all is matched then final value is matched; otherwise isn't;
- If there are more than one precondition defined for an engine then each precondition is evaluated with steps described above.
- Final stage of preconditions checking is verifying if all precondition are matched. If any is not matched then further action is not performed and results are not calculated (they get null values) and algorithm goes to next iteration of action dimensions. Otherwise algorithm proceeds further.
- Back to action dimensions iteration – now its time to perform engine action.
- All action options are evaluated. If all options have values then using these values engine action is performed. Otherwise action is not performed and results are not calculated (they get null values) and algorithm goes to next iteration of action dimensions.
- After action is done results are calculated. For each result additional iteration is done by additional dimensions that is a subtraction of action from result dimensions. Then result value is calculated and assigned to corresponding result macro using current indexes of action dimensions and additional result dimensions. Thus final dimensions of the result macro is a union of action and result dimensions. If result doesn't have value because of any included macro doesn't have value then this iteration is skipped thus result macro will not have value for this iteration too.
- Algorithm goes to next iteration of action dimensions.