A dependency is a global variable (the dependent variable) and an associated definition that is like a function with no arguments. Values can be explicitly set and referenced in exactly the same ways as for a global variable, but they can also be set through the associated definition.In this chapter the basic characteristics of dependencies are developed through a series of examples, and are then collected in a definition of dependencies. Then, itemwise dependencies are defined and exemplified. Finally, cyclic dependencies are discussed.
A dependency is created by specifying its definition, in the form name:expression. The variable name can be either new or pre-existing. The body of the definition (which follows the colon) is entered in the same way that the body of any defined function is entered, with the same syntax rules, and the variable name can appear within it. The description of Execute in Context shows how you can define dependencies of the same form in several contexts at once. Because a dependency definition contains a colon, it cannot be a statement in a defined function or another dependency definition. To create a dependency within such a definition, use Execute in Context or the like.A dependency, both variable and definition, can be deleted, and its storage freed, by the Expunge function (_ex) or command ($ex). The Remove Dependency Definition function, (_undef) and command ($undef), as their names imply, delete only the definition, leaving an ordinary variable with all its other properties intact, e.g., its value and any callback function set on it (see "Callback Functions"). They do not cause an evaluation before they remove the definition.
The first time a dependency is referenced after it is created, (assuming it has not been explicitly specified in the meantime) its definition is evaluated and the result is both saved and returned. When the dependency is referenced again, the saved value is returned if it has not been marked invalid in the meantime; otherwise the definition is evaluated and the new result is saved and returned. The saved value can also be set through ordinary Assignment, and this value will be returned until it is marked invalid. For example:aû3 ã Define a global variable a b:a*2 ã Define a dependency b b 9 ã The definition of b is evaluated. b 9 ã The saved value of b is returned. aû4 ã The saved value of b is marked invalid. b 16 ã The definition of b is evaluated. bû13 ã Specify a value for b b 13 ã The saved value of b is returned. aû5 ã The saved value of b is marked invalid. b 25 ã The definition of b is evaluated.The rules of localization within the definition of a dependency are the same as those for ordinary functions.When a dependency is defined, the value, if any, of the dependent variable is marked invalid - not erased, just marked invalid. After that, the saved value (which continues to be retained) is marked invalid (evaluation needed) whenever the source of a value or the dependency itself is changed - i.e.,
A visible use of a name occurs when the name appears in A+ code directly, and not in a character string or a symbol. A use that is not visible is an implicit reference, which occurs through the use of Execute (â) or Value (%).
- the value of a visibly used global variable is changed;
- another dependency visibly used in the definition has its value marked invalid or explicitly set;
- the definition of a visibly used function or operator is modified;
- the dependency is redefined.
For example, if
aû100 b:a*2 f x:3+x df:a+b+f 2000 df 12103 ã The definition of df is evaluated.then any one of the following will cause the value of df (saved by the last input line) to be marked invalid:aû50 b:a*3 bû625 f x:4«x df:a+b+f 3000Only changes to global variables, functions, and dependencies that have a visible use will cause the saved value of a dependency to be marked invalid: implicit references will not. Moreover, changes to global variables that are assigned but not referenced in a dependency definition will not cause its saved value to be marked invalid. For example, in the dependency df defined below, changing x will cause the saved value of df to be marked invalid, but changing y or z will not, because there are no visible uses of them, and c is only set, not referenced (it is the right argument of the specification, (â'y')+%`z, that is referenced). Entering a new definition of df will of course mark any saved value invalid.df:x+.cû(â'y')+%`z ã The saved value of df is marked invalid. xû10 yû100 zû1000 df 1110 xû20 ã The saved value of df is marked invalid. df 1120 yû200 ã df not marked: no visible use in the definition. df 1120 zû2000 ã df not marked: no visible use in the definition. df 1120 cû¢50 ã df not marked: in the definition c is only assigned, not referenced. df 1120When a dependency definition is executed, the dependent variable is first marked valid. This validation allows the variable to be referenced during evaluation, either within the definition or in asynchronous execution such as a callback, and it provides a fallback value, which may possibly be of some use.If execution of a dependency fails, a suspension occurs, as here, where n has no value:
m:3«n m .n: valueWhen the suspension is cleared, one of two things happens; if the dependency does not have a saved value, as in this example, then a value error on its name occurs:* ý .m: value *If, however, it has a saved value then that value has been marked valid. It is returned when the suspension is cleared. Continuing the example:* ý ã Clear the previous suspension in this example. mû5 ã Now m has a saved value that is not marked invalid. nû'a' ã Marks m's saved value invalid and makes its definition erroneous. m «: type ã The saved value has now been marked valid. * m * 5 * ý ã Clear the suspension. 5 ã The saved value is returned.More examples of dependencies are presented in the chapters that follow.
A dependency is a global variable, the dependent variable, with an associated niladic definition. It is established byname:definition or name[indexname]:definitionThe latter form is an itemwise dependency and is treated separately below.The rules of localization for a dependency definition are the same as those for ordinary functions, and indeed it is interpreted exactly as the body of a niladic function would be. name may appear within definition.
When referenced, the value of a dependency is determined as follows:
A dependency that is displayed by s in any sector or workspace and is not iconized is referenced each time A+ goes through its main loop, and likewise if it is bound to the `reference class.
- If it has no valid saved value, its definition is evaluated and the result of the evaluation is the value, which is also saved for future references. If it has an invalid saved value, the invalidation is removed as the first step of evaluation, and if the evaluation fails this saved value is returned when the execution suspension is cleared. If the dependent variable is bound to a display class and the definition yields an improper value for that class, then the saved value will be retained (no longer marked invalid, of course), in some cases with no warning or error message.
- If the dependency has a saved value that is not marked invalid, the saved value is the value.
Note that if a dependency has a stored value that is marked invalid, a reference triggers an evaluation not only when the reference requires the value, as in `dep is `table, but also when the reference does not require the value, as in `class of `dep (supposing `dep not shown, so that its invalidation did not trigger an immediate evaluation).
The saved value of a dependency is marked invalid:
A global object is visibly referenced if its name occurs as the source of a value (not just as a specification target) outside an argument to Execute or Value. A change to such an object occurs when:
- when it is first defined (if it has a saved value), and when its definition is modified;
- when a change occurs to a global object that is visibly referenced (see below) in its definition, and the definition is not currently being evaluated, nor a callback on the dependency currently being executed.
Note that a global variable that is set in a dependency but never referenced is not a visibly referenced object.
- a global variable is explicitly modified (a mapped-file global variable can change value without itself being explicitly changed);
- the saved value of a dependency is marked invalid or its saved value or definition is explicitly modified;
- the definition of a function or operator is changed.
The value of a dependency is marked valid just before its definition is evaluated (if there is a saved value) and whenever a value is explicitly Assigned to the dependent variable - including, of course, at the end of a successful execution of its definition.
A change to a visibly referenced global variable made during evaluation does not cause the dependent variable's value to be marked invalid. Consider
m:{mûm+n; (n)û10«n; m+n} mû100 nû1 m 111nû1 marks the saved value of m invalid, so the next statement triggers evaluation of its definition. The invalidation mark is changed to an under-evaluation mark, and mûm+n uses the previous saved value to produce a new saved value (101). Respecifying n (as 10) at this point does not invalidate this new saved value, since it is marked as under evaluation, and it is used in the final m+n.On the other hand, a new definition unconditionally invalidates a saved value, as in
m:{mûm+n; â"m:n"; m+n} mû100 nû1 m 2where in the final m+n the new definition (m:n) is executed to evaluate m.
Itemwise dependencies are intended to reduce redundant computation by allowing the items of a dependent variable to be marked invalid separately. Only the first axis is singled out in this way. The form is the same as for ordinary dependencies except that Bracket Indexing and an index name are used in the definition:name[indexname]:body_of_definitionThe variable named by the index name is local and can be used for any purpose within the definition. Since this variable is local, the index name must be unqualified.A simple example of an itemwise dependency is
y[i]:f{m[i];c}Whenever c is changed - even just one of its items -, all of the saved value of y is marked invalid, but when items of m are appropriately changed, then only the corresponding items of the saved value of y are marked invalid.
For itemwise invalidation of the dependent variable, any change in a variable on which the dependency depends itemwise must be made by Bracket Indexing, Choose, or Append Assignment. In the example just shown (leaving aside for the moment an incompatibility between Append and the others), eitherm[3756]ûexpression or (3756#m)ûexpressionmarks just y[3756] invalid, and, more generally, each of((expr1)#m)ûexpr2 and m[expr3]ûexpr4 and m[,]ûexpr5marks items of y invalid.All of y, however, is marked invalid by
((3756=É#m)/m)ûexpression and, of course, m[;5]ûexpressionMoreover, either pair of statementsm[3756]ûexpression m[,]ûexpr5 or m[,]ûexpr5 m[3756]ûexpressionmark all of y invalid (assuming no updating of y between the statements). In effect, when y is updated, it is updated by just one of a Bracket Assignment, an Append Assignment, or an ordinary Assignment. Therefore, itemwise invalidations caused by a Bracket Assignment and an Append Assignment cannot be pending at the same time. (Itemwise invalidations from several Bracket Assignments or several Append Assignments can be pending simultaneously, of course.)If a is a large itemwise dependency and you know at some point in the code that you may be about to cause a total invalidation in one of the two manners described just above, you might consider avoiding the total invalidation by forcing an itemwise evaluation with a trivial statement such as
{Òa;};
In an itemwise dependency definition, only the form shown, Bracket Indexing with just an index name and no semicolons, is allowed on the left. If any other form appears, the dependency is total, no matter what the rest of the definition is like.In the body of the definition, to the right of the colon, a similar, but slightly less stringent, rule holds for each variable. To have the dependency depend itemwise on a variable, every referencing of that variable must be a Bracket Indexing using just that same index name for the first axis. None of the following definitions will allow updating of less than all of ns:
ns[i]:(sr+nw)[i] ã An expression, not a variable, is being indexed. ns[i]:{jûi;sr[j]+nw[j]} ã The index has same value, but not same name. ns[i]:(i#sr)+i#nw ã Choose, not Bracket Indexing, used in the def. ns[i]:sr[i+1]+nw[i+1] ã Indices consist of more than just index name. ns[i]:sr[i]-sr[i-1] ã The bad occurrence negates the good one.On the other hand, indexing along other axes does not interfere with itemwise invalidation:ns[i]:sr[i;É50],@1 nw[i;50+É50]allows updating of only the affected items of ns. Obviously, any dependence on a function or operator is total.Note: At the present time, the parser may confuse the dependent variable with a function when parsing the body of an itemwise dependency definition. If such a parsing error occurs, replace the dependent variable, b, say, in the body by its fully qualified form, cxt.b, say, if the context is known and otherwise by %`b (or more likely (%`b)) to give the parser the hint it needs.
When an itemwise dependency is defined or its definition is changed, its saved value, if any, is marked invalid, just as is done for any dependency. If the next event for it is a reference, its definition is evaluated, with the index name being given the value Null, to indicate all items (see next section). If at some later time any change occurs in a function, operator, or variable on which it depends, and that change is not recognized as itemwise, then the saved value is again marked invalid. If still later the definition is evaluated, the value of the index name is again the vector Null (again, see next section). Just as for an ordinary dependency, an evaluation with Null index for a variable bound to a screen display class can lead, sometimes with no warning or error message, to a retention of the saved value (no longer marked invalid).Now suppose a dependent variable is referenced after a series of changes that are recognized as itemwise to variables on which it depends itemwise. (Indices modified by a callback may not be recognized.) Suppose further that no total invalidation has occurred. Then the dependency definition is evaluated with an index vector consisting of the indices for which the changes were recognized, listed in the order in which the changes took place, but without duplication. This vector may contain all indices of the dependent variable; it will nevertheless not be transformed into the Null. Just as for a total invalidation, an evaluation for a variable bound to a screen display class can lead, sometimes with no warning or error message, to a retention of the saved value (no longer marked invalid). Furthermore, for any dependent variable, evaluation with a value that is impermissible for Bracket Indexing leads to validation of the saved value; e.g., if int[i]:fl[i] and fl is set to a floating-point vector, int is set to an integer vector, fl[2] is set to a value that cannot be coerced to an integer, and then int is referenced, the evaluation fails and the saved value of int (no longer marked invalid) is returned, without any error or warning message.
For any vector of indices i that is not the Null, the itemwise dependency definition a[i]:... can be thought of as being executed in the form a[i]û... . When the index vector is the Null, however, this view would be incorrect. It is, rather, executed in the form aû... . The shape of a can be changed by a total invalidation.To repeat, for emphasis: when there is a recognized total evaluation of an itemwise dependency:
- The Null is used to index the variables for which the dependency is itemwise.
- The result of executing the body of the dependency definition is the new saved value of the dependent variable. The Assignment to the dependent variable is not an Indexed Assignment, but rather an ordinary Assignment, and the shape of the dependent variable can be changed by it.
So far only acyclic dependencies have been discussed, i.e., sets of dependencies with no recursive references, where no dependency depends on itself. The Debugging State system command ($dbg) provides a useful tool for analyzing recursive, or cyclic dependencies.Example 1. Evaluating Cyclic Dependencies
The first example illustrates an important property of cyclic dependencies: if in the course of evaluating a dependency, its name is recursively referenced, that reference will be satisfied with the previously saved value (if there is no saved value, a value error occurs). Unlike a recursive function, a cyclic dependency cannot cause an infinite recursion. For example:
$dbg dep 1 ã Show dependency evaluations. a:b+2 ã Marks the saved value of a invalid. b:a+g+2 aû12 ã Assign a and b values to avoid value errors. bû5 ã They are now not marked invalid. gû10 ã Saved value of b is now marked invalid, so a is also. a ã Dependency .a evaluation entered ã Dependency .b evaluation entered ã Dependency .b evaluation exited ã Dependency .a evaluation exited 26The new value of a is 26, which means the new value of b is 24. Since g is 10, a must have been 12 when b was evaluated, which was its saved value at the time.Example 2. Interrelated quantities (rate, yield, spread)
Cyclic dependencies arise quite naturally in applications as sets of interrelated variables. For example, consider the following mutual relationships among underlying rate u, yield y, and spread s:
y:u+s u:y-s s:y-uFor initialization set any two of these quantities. After that, if any one of the quantities is set the other two will be automatically updated when referenced. For example:$dbg dep 1 (u;s)û(0.08;0.005) ã Set both at once else one invalidates the other. y ã Defining dependency marked any saved value of y invalid. ã Dependency .y evaluation entered ã Dependency .y evaluation exited 0.085 yû0.09 ã Marks the saved values of both u and s invalid. u ã Dependency .u evaluation entered ã Dependency .s evaluation entered ã Dependency .s evaluation exited ã Dependency .u evaluation exited 0.08 s 0.01 ã The saved value is returned.When y was reassigned, the saved values of u and s were marked invalid. When u was then referenced its definition was evaluated, causing s and y to be referenced. The reference to s caused it to be evaluated, using the new value for y and the old value for u. This new value for s was then used with the new value of y to produce a new value of u. Because of the selfconsistency of the mathematical expressions, the new value of u is the same as the old one. However, these computed values would not be the same if u and s had been referenced in the opposite order. To verify this, reproduce the example up to the references of u and s, and then reference them in the opposite order:(u;s)û(0.08;0.005) y ã Dependency .y evaluation entered ã Dependency .y evaluation exited 0.085 yû0.09 s ã Dependency .s evaluation entered ã Dependency .u evaluation entered ã Dependency .u evaluation exited ã Dependency .s evaluation exited 0.005 u 0.085Reasoning as before, u now has a different value from the one originally specified, but s does not. Of course it is not always possible to know the order in which dependencies will reevaluated, which means that one cannot be certain of the new values in cases like this. The only consistent way to use n cyclic dependencies like these is to explicitly set any n-1 of the quantities and use the dependent definition only for the remaining one.
The system functions and commands discussed here are described in "System Functions", and "System Commands".The following example will be used to illustrate the system functions and system commands that apply to dependencies.
pû2 3Ò1.23 4.5 20 5.6 7 8.95 p 1.23 4.5 20 5.6 7 8.95 nû2 3Ò10 1 2 5 3 1 n 10 1 2 5 3 1 fn{x}:ÄxIn the definitions that follow, m is dependent on p, n, and fn; ct is dependent on m; and gt is dependent on ct.m:p«fn{n} ã Price times number. ct:+/m ã Column totals. gt:+/ct ã Grand total.The system command $deps lists the names of the dependencies, while the system function _nl provides such a list as a vector of symbols when given `deps as an argument:$deps m ct gt listû_nl{;`deps} list `m `ct `gt $vars p n _nl{;`vars} `p `nDependencies are global variables, and when they have saved values, their names appear in variable lists.ct 40.3 25.5 48.95 $vars p n m ct ã The evaluation of ct caused evaluation of m, but not of gt.The system command $def dep displays the definition of the dependency dep, while the system function _def `dep returns that definition as a character vector:$def gt gt:+/ct defû_def `gt def gt:+/ctThe system command $dep name lists all dependencies in which name is explicitly referenced, where name can be any name, but the only meaningful names are those of global variables, dependencies, or functions. The corresponding system function is _dep:$dep n m $dep fn m _dep `ct `.gtThe system function _alldep is the transitive closure of _dep with duplicates removed. The value of _alldep `name is a list consisting of the unique names in _dep `name, _dep¡_dep `name, etc._alldep `m `.ct `.gtThe command $undef v and function _undef v remove the dependency definition for v while leaving all the other properties of v intact. Forcing evaluation of v just before such a removal will save its latest value.Finally, there is the Debugging State system command $dbg. The complete definition is given above, in the chapter on system commands. The use illustrated here is $dbg dep 1 for tracing dependency evaluation. Whenever a dependency is referenced, an "entered" and an "exited" message is displayed for each dependency whose definition had to be evaluated in order to satisfy the reference. In the above example, ct has already been referenced. If it is referenced again, its saved value is returned, so no dependencies are evaluated. If gt is referenced, however, its definition will be evaluated.
$dbg dep 1 ct 40.3 25.5 48.95 gt ã Dependency .gt evaluation entered ã Dependency .gt evaluation exited 114.75If one of the underlying variables is changed, all the dependencies are marked for evaluation, so all their names appear when gt is subsequently referenced:n[1;1]û4 gt ã Dependency .gt evaluation entered ã Dependency .ct evaluation entered ã Dependency .m evaluation entered ã Dependency .m evaluation exited ã Dependency .ct evaluation exited ã Dependency .gt evaluation exited 121.75Note that had m, ct, and gt been defined as niladic functions, the evaluated results illustrated in this example would have been the same. However, niladic functions have no saved values and are therefore always evaluated when referenced. Consequently dependencies provide a generally more efficient evaluation scheme.
doc@aplusdev.org | © Copyright 19952008 Morgan Stanley Dean Witter & Co. All rights reserved. |