This manual will make use of the following abbreviations: ctx for context, dim for dimension, isct for intersection, p or pt for point.
Knowledge is represented by binding the intersection of two or more dimensional points to a value point. One way of conceptualizing this is through the intersection of two or more points in a multidimensional space. At that intersecting point is the value point. The process of associating the intersection of points to a value is called binding. Note that an intersection is itself a point along the Intersection dimension.
The syntax for specifying an intersection is [p1 p2 ... pn] where pi are the points to be intersected. A binding is done by associating a value point to an intersection. There are several ways to do this in V4.
V4 defines a special dimension, IntMod, and points along this dimension are internally defined modules which the V4 runtime can execute. Syntactically, the user references an internal module like a procedural language module, that is name(p1 p2 ... pn) where name is the IntMod name and pi are the argument points. Internally, internal modules are handled as a two point intersection. The first point being the internal module point and the second being a list of points corresponding to its arguments.
An evaluator is a process, which given an intersection, returns the value point associated with that intersection. If the value is itself an intersection then the process (usually) repeats until a non-intersection value is returned.
Bind [Salary Emp:123] Int:25000 | bind NId:Salary and Emp:123 to Int:25000 |
Bind [Benefits Emp:123] Int:4000 |
= [Salary Emp:123] | evaluate the intersection of NId:Salary and Emp:123 | |
25000 | V4 outputs 25000 |
Bind [Cost Emp:123] Plus( [Salary Emp:123] [Benefits Emp:123] ) |
= [Cost Emp:123] | evaluate the intersection of NId:Cost and Emp:123 | |
29000 | V4 outputs the sum of the two nested intersections |
In the above example, the cost of employee 123 is defined to be the sum of his salary and benefits. The V4 interpreter evaluates the two nested intersections to Plus and the Plus intmod itself to return the answer of 29000.
A context is an independent set of points with the constraint that no two points belong to the same dimension. Points can be added to the context. If a point is added to the context and another point of the same dimension already resides in the context then the new point replaces the old. For example if Int:20 were in the context and Int:15 was added, then the Int:20 would be replaced with Int:15. A point in the context can be referenced as dim* which means the point value for dimension dim in the context.
The context also supports a frame construct. This is the ability to, in effect, create a stack of linked contexts. Every context frame behaves as a new context set. Referencing a point in the entire context is done by searching the current frame for the dimension, then the prior frame, then the prior-prior, etc. When a frame is removed from the context, all points associated with the frame are also removed.
Context Add Int:123 | add the point Int:123 to the context | |
Context Push NewFrame | create a new context frame called NewFrame | |
Context Add Int:456 | add Int:456 to the context | |
= Int* | determine current Int point on context |
456 |
Context Pop | pop NewFrame off of context | |
= Int* | determine now current Int point |
123 |
In this example, Int:123 is added to the context, then a new frame is created and Int:456 is added. Since a new frame exists with no points, the Int:123 is not replaced but Int:456 is simply added to the new frame. The command 'Context Pop' removes the most recent frame and all points associated with it leaving Int:123 in the context.
A point can be explicitly removed from the context by using the dim:{undefined} construct. Adding this point replaces a defined point on dim with the undefined point. Note that dim~ may also be used.
Bindings may also be specified with an undefined point-
Bind [xyz dim:{undefined}] value |
The above binding will match only if dim is undefined in the current context.
See also 'Removing Context Points after Binding Match' in the Advanced V4 Concepts section.
Much of the power of the V4 model comes from the ability to evaluate an intersection in a given context. Instead of completely specifying all points in an intersection to be evaluated, V4 permits partial specifications and pulls the remaining points from the current context. This is not easy to comprehend so a few simple examples are in order.
Bind [Name Cus:123] "John Smith & Sons" Context Add Cus:123 = [Name] Alpha:"John Smith & Sons" |
In this example Cus:123 is added to the context and the intersection [Name] is evaluated. V4 automatically incorporates all points in the context into the evaluation to come up with the answer. To paraphrase, the evaluation of [Name] is asking 'In the current context what is the name?' and since the context contains Cus:123 we are currently referring to that customer, thus the name in question must be that of customer 123.
The use of the context in evaluating intersections paves the way for a new class of programming environments that can dynamically react to a varying context. Programs need no longer require data to be explicitly defined and referenced. Instead programs can request data by its generic name and let the current context fill in the specifics. For example a context point of language (Language:English, Language:Spanish) can be used to change the prompts and messages on a screen for different users. The dimension point of Calendar (Calendar:Fiscal, Calendar:Yearly) can be used to show sales based on a fiscal year or a calendar year for different users in a large company. The use of dimensions for Prompts, ScreenPosition, and Format can be used to change the way screens and data are formatted based on user, group, or company preferences.
This capability unfortunately does not come without a price and the two big issues to resolve are performance and ambiguity. The performance issue is getting the evaluator to work at the speeds needed to make this a usable product. As it turns out this is not as hard as it may seem and V4 is currently doing many real world problems on PC based platforms. The ambiguity issue can be demonstrated by the simple example-
Bind [Name Cus:123] "John Smith & Sons" Bind [Name Vendor:543] "Smith Supply" Context Add Cus:123 Vendor:543 = [Name] |
??? | What name- vendor or customer? |
Here we have both a customer point and vendor point in the context. What name do we return? The ambiguous nature of this request will be examined later in this manual. Suffice to say that ambiguity can be controlled and in some cases the availability of more that one answer is useful.
V4 provides many syntactic shortcuts to reduce the size of rules. This section briefly describes some of these.
Although the formal syntax for a point is dim:pointvalue, it is not always neccesary to specify the dimension. The following examples demonstrate how V4 will default a dimension based on the format of the value-
123 | Int:123 (V4 assumes an integer value is on the Int dimension) | |
123.45 | Num:123.45 (Real numbers default to the Num dimension) | |
Now is the time | Alpha:"Now is the time" (String literals default to the Alpha dimension) | |
+123 | Delta:+123 (Integer prefaced with a plus sign default to the Delta dimension) | |
-123 | Delta:-123 (Ditto for minus signs) | |
Sales | NId:SALES (Symbols default to the NId dimension) | |
(pt1 pt2 ... ptn) | List:(pt1 ... ptn) (PointTs enclosed in parentheses are assigned to List) |
Two points should be noted. First that the example above of -123 references a point on the Delta dimension, not the Int dimension. Since Delta:-123 and Int:-123 are maintained identically internally this should cause no problems using negative integer literals. Secondly, symbols default to the NId dimension unless another dimension has been assigned to the symbol with the Point command.
Strings enclosed with single quotes are processed by the V4 parser in a special manner. If the string matches any of the patterns as defined by the Recognize command then the string is replaced. For instance, the command
Recognize '([0-9]+)/([0-9]+)' 'DTInfo(UDate:{now} Month::\1 Day::\2)' Eval |
defines a parser pattern that recognizes single quoted strings of the form 'mm/dd' and replaces them with valid V4 points on Dim:UDate for the month and day of the current year-
[SalesToDate '6/15'] | is equivalent to [SalesToDate UDate:030615] (if parsed in the year 2003) |
The dim.. construct denotes all points on a dimension. When it is used in a binding it means that all points on the dimension can be matched. When it is used as a value it represents all the points currently defined on that dimension. This form may only be used on dimensions created with the PointCreate attribute.
Bind [Sales Year..] Sum(EnumCL(Cus.. @[YearlySales Year*])) |
The example above states that Sales for any year (Year..) is defined as the sum of YearlySales for that year for all customers (Cus..). Note how the '..' takes on slightly different meanings on the left and right hand portion of the binding.
The NId dimension is the default dimension for identifiers. It is possible and sometimes useful to reference NId.. in the left hand side of a binding. However doing this may result in much programmer grief unless great care is given. For this reason, the V4 compiler issues a warning whenever it sees the construct 'NId..' within a binding intersection. This warning may be suppressed by using the 'NId...' construct. It is identical in function to 'NId..' and its only purpose is to eliminate the compiler warning.
The asterisk is used after the dimension to reference the current value of a dimension. As with '..' above, it can have slightly different meanings as shown by the following example-
Bind [Age Emp*] Int:43 Bind [Age Emp..] Div(Minus(Date:{now} [DateOfBirth Emp*]) 365) |
In the first line, the age of the current employee is defined as 43. In the second example, the Emp* refers to the employee matched by the [Age Emp..] binding, or the current employee at the time of evaluation, not binding.
The double asterisk is used after the dimension to reference the 'prior' value of a dimension. By 'prior' we mean the contextual value not in the current frame but in one of the prior frames.
New points on dimensions declared with the 'PointCreate New' attribute can be created with either the MakeP primitive or the dim+ construct.
Context(EDim+) |
The above example creates a new point on the EDim dimension and adds it to the current context.
A point may be removed from the context by adding the undefined reference to the dimension- dim~.
[pointa pointb ... | dim~] |
Evaluates the intersection [pointa pointb] with any points on Dim:dim first removed from the context.
A point may be removed from the context on a binding match with the dim~ construct.
Context Add 123 | Add the point to the context | |
[Int.. square] {Int* * Int*} | Define 'square' of an integer | |
[~Int.. squareRmv] {Int* * Int*} | Define it again with auto-removal of Int point | |
= [square] | returns 15129 | |
= [squareRmv] | also returns 15129 | |
= Int* | fails because the Dim:Int point was removed from the context on the evaluation of [squareRmv] |
Many programming languages support a dot construct that indicates a parent-child relationship or structure-field relationship. For example, Cus.Name references the Name element of the Cus structure (or relation). V4 has no such explicit parent-child relations but can achieve the same results. The example-
Enum(Cus.. @EchoT([Cus* Id] [Cus* Name] [Cus* State])) |
enumerates through all customers and outputs each customer's Id, Name and State. V4 implements a dot structure that allows you to mimic the traditional parent-child meaning. With V4 dim.point is taken to mean [dim* point]. With this we can rewrite the above example as
Enum(Cus.. @EchoT(Cus.Id Cus.Name Cus.State)) |
which is less to type and much easier to read. The V4 compiler saves the results of the previous dot construct and permits abbreviations such as
Enum(Cus.. @EchoT(Cus.Id .Name .State)) |
where only the first reference needs the 'Cus' prefix. Multiple nesting are allowed- the following two examples are identical-
Order.BillTo.SlsRep.Manager.Name [Name [Manager [SlsRep [BillTo Order*]]]] |
It is sometimes necessary to convert a point in one dimension into the equivalent point in another. This can be done with the Project module or, more easily with the ':=' construct. For example a floating point number can be converted to an integer (with rounding) in either of these two ways-
Project(Dim:Int Float:123.567) | returns Int:124 | |
Int:=Float:123.567 | also returns Int:124 |
This form of projection is similar to the dim:=point format except that the arguments are reversed. This form permits the user to follow the dim with a period ('.') and then other points.
a.b->c.d | is the same as [Project(Dim:C [a* b]) d] |
The EnumCL module is used to, in effect, convert one list into another. The form of this module is
EnumCL(list @isct) |
The action performed is to enumerate through each point in list, evaluate isct and create a list of the results of each evaluation. This list is then returned. An alternative way of doing this is with
isct/list |
which is internally converted to an EnumCL call.
A single quote can be used in a binding in place of the dot-dot construct. For example, the following two examples are equivalent-
Bind [Name Cust..] AggVal(Cust* Dim:Alpha 3) Bind [Cust's Name] AggVal(Cust* Dim:Alpha 3) |
The possessive may also be used within an intersection to be evaluated. The next four statements are also identical-
Bind [Sale's State] [State Sale's ShipTo] Bind [Sale's State] [State Sale.ShipTo] Bind [Sale.. State] Sale's ShipTo's State Bind [Sale's State] Sale.ShipTo.State |
Many types of database queries access the same database information several times. With V4, each access results in one or more evaluations. If hundreds of thousands or millions of points are being scanned or tallied then anything to reduce evaluations can improve performance. V4 supports a caching facility that lets the user determine when to cache and when not to. It would be better to have V4 automatically cache but because of the contextual nature this is not yet possible. The following example selects and tallies order by month-
Bind [SumUpOrders Month..] Tally( (Sum::Order.Amount By::DTInfo(Order.Date UMonth?) Bind::[MonthSales]) ) |
Evaluating SumUpOrders will evaluate 'In(DTInfo(Order.Date UMonth?)' twice for each order selected. If there are hundreds of thousands of orders then a better way would be to-
Bind [SumUpOrders Month..] Tally( (Sum::Order.Amount By:: Bind [Order.. Month] DTInfo(Order.Date UMonth?) |
In the second example, the cached evaluation
It is often useful to evaluate or return a secondary point if the primary intersection to be evaluated fails. For example if we wanted a customer's sales history, by month, for the last three years we might evaluate the list-
EnumCL( Month:9401..9612 @[Sales Customer*]) |
which would evaluate the sales for the current customer over all months specified and return the list. However if we did not have any binding/record of sales for the customer for a particular month then we would get an error during the evaluation. This can be corrected with the Def module which tests to see if an intersection evaluates-
EnumCL( Month:9401..9612 @Def(@[Sales Customer*] 0)) |
The Def module would return the sales if it evaluated otherwise 0. This works but is alot to enter. To get around this 'problem', the V4 evaluator will evaluate several intersections delimited by commas in an attempt to return a value. If the first intersection in a comma delimited list of intersections does not evaluate, V4 will evaluate the second. If it fails V4 will continue with the third. This continues until the end of the chain (the evaluation fails) or a successful evaluation takes place. Our example from above could be written as-
EnumCL( Month:9401..9612 @[Sales Customer*],0) |
The ability to chain intersections as described above provides a localized method for dealing with evaluation failures. V4 also provides a more global, flexible approach to dealing with intersection evaluation failures. Bindings of the form-
Bind [UV4:IsctFail ...] value |
can be specified. These cause V4 to attempt to evaluate [UV4:IsctFail] in the context of all of the points in a failed intersection. For example suppose the following binding has been declared-
Bind [UV4:IsctFail Name Cus..] Str("Cus #" Cus* "??") |
and the intersection
Cus.Name |
fails, then V4 will attempt to evaluate [UV4:IsctFail] in the context of NId:Name and the current customer. The evaluation would then match the above binding and return "Cus #nnn??" as the name of the customer.
If the binding containing the UV4:IsctFail fails and is followed by another chained intersection (see above) then this processing is disabled during the evaluation of the chain, i.e. this is not handled recursively.
It is sometimes useful to evaluate an intersection in a particular context. While V4 modules exist to do this, it is useful to be able to specify a context as part of the evaluated intersection. The syntax for this is-
[ point1 point2 ... | context1 context2 ... ] |
where the pointi are the points to be evaluated and contexti are points to be added to the context for the duration of the evaluation.
V4 uses what is known as prefix notation for most of its operations. For instance to add two numbers, V4 uses 'Plus(num1 num1)' rather than 'num1+num2'. Some users find this awkward and prefer to use the more traditional infix notation. This can be done within V4 by enclosing an expression in braces. The following two examples are identical.
If::GT(Order.Amt Div(Cus.YTDSales 1000)) If::{Order.Amt > Cus.YTDSales/1000} |
Either method is acceptable and can be freely intermixed. The following table lists the allowed operators and V4 internal module equivalents
Table 1 - Infix Operators
+ | Plus | = | EQk | |
- | Minus | == | In | |
* | Mult | < | LT | |
/ | Div | <= | LE | |
// | DDiv | > | GT | |
% | Percent | >= | GE | |
~ | Not | <> | NE | |
| | Or | & | And | |
~= | nIn |
A dimension can be given multiple synonyms. If a dimension is referenced which is currently undefined then the V4 interpreter will attempt to evaluate-
[UV4:DimSynonym NId:dimension] |
where dimension is the name of the synonym. If the evaluation succeeds and returns a valid dimension point then that is used as the actual dimension.
The CSMD model represents concepts as dimensions and instances of concepts as point on dimensions. The theoretical notion of none-ness seems contrary to the model. Why have a point to represent no points on a dimension? Pragmatically though, it can be quite useful. For instance one way to model an input form is to assign a different dimensions to each input element. Entered values become specific points on each of the dimensions. But what if a date or numeric element is blank? What point on the date/numeric dimension should be assigned? V4 resolves this issue by defining none values for many types of points including time and numeric. Alpha points do not require a none value because an empty string is a valid point.
The none values for time points is implemented with special values that cannot be confused with valid time points. For example, UDate:none is stored as zero (UDate:#0) because that is not a valid date. Other date-time points implement the none value in a similar fashion.
Numeric points cannot be handled this way because there are no inherently invalid values for numbers. Numeric none values are treated as special point values within V4, not as a specific numeric value. Nonetheless one might want a numeric none value to be considered identical to a particular numeric value. This can be done when a dimension is declared by specifying the NONE attribute within the Dimension command.
Projecting a none value into an integer or real point forces the point into its assigned none-value. If no such value has been defined then the projection fails.
Dim Value Integer None 0 | Defines a Value dimension, substitute Value:0 for Value:none | |
Context Add Value:none | Add point to context | |
EQ(Value* Value:none) | results in Logical:True | |
{123 + Value*} | results in Value:123 | |
Int:=Value* | results in Int:0 (not Int:none) |
All evaluations within V4 are triggered by intersections and internal modules. Occasionally it is convenient to be able to force the intersection of a point. This can be done by declaring the AutoIsct attribute to a dimension. The following example show how-
Dim Field AutoIsct Point Field Name Address City |
Bind [Cus.. Name] AggVal(Cus* Dim:Alpha 3) | Define name of customer as third field in aggregate | |
Context Add Cus:xxx | Add customer xxx to context | |
= Name | As if entered [Name] |
Sometimes it is necessary to determine if a binding or rule exists on a specific point or dimension. This typically occurs in V4 bindings that are used to auto-construct other V4 bindings. The Def module can be used to see if an intersection is defined but the problem becomes selecting points to use to test. For example, suppose we had bindings such as-
Bind [Order.. Column:Date] value |
which defines the date of an order. Suppose we wanted to write a general rule such as
Bind [TestDim.. Column..] true-or-false |
which would return true if the Column point was defined on the shell dimension TestDim. We have no way of knowing what points can be associated with the dimension specified by TestDim- integer? date? alpha? ... To resolve this problem, V4 supports a 'sample point' for each dimension allowing our test to be written as-
Bind [TestDim.. Column..] Def(MakeI( MakeP(TestDim* Special::Sample) Column*) Logical:True Logical:False) = [TestDim:Dim:Order Column:Date] |
The evaluation tests whether or not '[Order:{sample} Column:Date]' is defined and returns true or false.
Once a point is inserted into the context it remains there until it is replaced by another point of the same dimension, it is explicitly removed, or the frame in which it resides is destroyed. It is also possible to have one or more points removed as a result of matching to a binding. Consider the example below-
Context Add dim1:100 | Add point to context | |
[whatIs dim1..] Echo("dim1=" dim1*) | Create binding | |
Eval [whatIs] | echos "dim1=100" | |
[readAndRemove ~dim1..] Echo("dim1 was " dim1*) | Another binding, note tilde prefix on dim1 | |
Eval [readAndRemove] | echos 'dim1 was 100' AND removes dim1 from context | |
Eval [readAndRemove] | fails, dim1 no longer in context |
The tilde prefix, '~', may only be used within an intersection. The dimension is removed from the context only after the succesful evaluation of the binding. If the evaluation fails, no dimensions are removed.
Inheritance is a powerful feature of object-oriented languages. It lets you define rules and facts for entire families of data types. A similar facility is available within V4 when a dimension is declared with the HasIsA attribute. Consider the example below-
Dim ShipDate UDate Dim OrderDate UDate Dim EntryDate UDate Dim PickDate UDate Here we have defined four dimensions, all of type UDate. If we wanted to define a rule that would give us the month associated with a date we could- Bind [ShipDate.. Month] DTInfo(ShipDate* UMonth?) Bind [OrderDate.. Month] DTInof(OrderDate* UMonth?) ... |
This would allow use to conveniently get the associated month with any of the dates, but at the expense of having a separate rule for each dimension. If we had multiple dimensions and multiple rules the number of bindings would increase with the product of the two. With inheritance we can simplify this. In the example below we define a new dimension, GenDate, which is our generalized date. We use a special binding to declare each of the date dimensions to be an instance of the generalized date-
Dim GenDate UDate Dim ShipDate UDate HasIsA Dim OrderDate UDate HasIsA Dim EntryDate UDate HasIsA Dim PickDate UDate HasIsA Bind [UV4:IsA ShipDate..] Dim:GenDate Bind [UV4:IsA OrderDate..] Dim:GenDate Bind [UV4:IsA EntryDate..] Dim:GenDate Bind [UV4:IsA PickDate..] Dim:GenDate Bind [GenDate.. Month] DateInof(GenDate* Month?) |
Here we only have to define one rule on GenDate, not separate rules for each dimension. In this example, it hardly seems worthwhile to add the four extra 'UV4:IsA' bindings to save on three 'Month' bindings. But as soon as we add another rule (such as Year!) we reduce the total number of bindings from eight (4 x 2) to six (4 + 2), with five rules we reduce the total number of binding from 20 (4 x 5) to nine (4 + 5).
The HasIsA attribute is not limited to a single level. If the declaration of dimension GenDate included a HasIsA then yet another level would be added.
It is important to note that 'UV4:IsA' bindings can be any form of V4 binding. We could just as well define ShipDate with several bindings-
Bind [UV4:IsA ShipDate:900101..961231] Dim:GenDate Bind [UV4:IsA ShipDate:>=970101] Dim:RecentDate Bind [UV4:IsA ShipDate.. WhatIf:Debug] Dim:DebugDate |
Here we have defined three different 'parent' dimensions for ShipDate based on date ranges and other contextual circumstances. This capability greatly extends the inheritance model found in classical object-oriented methodologies.
Sometimes an evaluation can result in more than one possible answer. In most cases, V4 returns what it considers the best answer and the fact that other possible answers were available is ignored. There are situations when it is necessary to work with more than one possible answer.
An example of this is in an order entry environment where a customer has been promised the best price. In any kind of large order-processing system, an item will have many possible prices depending on the item, the customer, the quantity ordered, the catalogue ordered from, other items in an order, and so forth an so on. In order to give the best price, one must be able to access all possible prices in a given situation and choose the smallest.
Another class of problems utilizes the ability of V4 to manipulate all possible matches in descending priority of goodness. To be able to reference the next best point is very useful in what-if scenarios. For example, suppose we are trying to determine if a new packaging system is worth the capital investment. The system can reduce shipping weight by 5% and costs $75,000. By reviewing shipments over the last n months and recosting we should be able to make an informed decision based on the amount saved over a given period. However our costing module is a black box which takes an order and returns the cost to ship it (weight is only a partial component of shipping costs, zones, shipping methods, time requirements all come into play). Rather than create a new order file with weights reduced by 5%, we would like to say, 'For this purposes of this scenario, reduce order weights by 5%. With V4 this can be done with:
Bind [Weight Order.. Scenario:FivePC] Mult( [-] Real:0.95 ) |
This binding defines the Weight of all orders (Order..) in this what-if scenario as being 0.95 times what the weight would have been ([-]) had we not matched this binding.
V4 supports the concept of time and the more general case of time varying points (such as the software releases or transaction processing) as a special type of dimension. In all other dimension types, the evaluator attempts an exact match of points to return a value. With points on a time dimension, V4 attempts a greater-than-or-equal match. For example:
Bind [Name Cus:123 Time:100] "Miss Smith" Bind [Name Cus:123 Time:150] "Mrs. Jones" Context Add Time:120 = [Name Cus:123] Alpha:"Miss Smith" |
In this example, the evaluator requires an exact match on the NId dimension (Name) and Cus dimension, but not on the Time dimension. In the context of Time:120, the proper name for Cus:123 is "Miss Smith". The example below demonstrates the use of time within V4-
Bind [MaritalStatus Person:John Time:10] "Single" Bind [MaritalStatus Person:John Time:20] "Married" Bind [MaritalStatus Person:John Time:30] "Divorced" |
Bind [What:IS List..] [`List*] | What:IS evaluates at current (context) time | |
Bind [What:WILL List..] [`List* | Time:{undefined}] | What:WILL evaluates at undefined (future) time | |
Bind [What:WAS List..] IsctVals( @[`List*] Int:2 ) | What:WAS evaluates R* at one match before current |
Context Add Time:25 |
[What:IS MaritalStatus Person:John] | Alpha:"Married" | |
[What:WAS MaritalStatus Person:John] | Alpha:"Single" | |
[What:WILL MaritalStatus Person:John] | Alpha:"Divorced" |
V4 supports an extensive suite of calcuations for UYEar, UQuarter, UMonth and UDate date types. The table below summarizes the allowed calculations. Note that these can be chained in any meaningful length.
Table 2 - Date Calculations
Start | Calculation | Description | Example | Result |
date | .num | converts the date to the numth day of the month | UDate:5/16/2008.10 | UDate:5/10/2008 |
date | +num | adds num to the date | UDate:current+3 | three days from now |
date | -num | subtracts num from the date | UDate:1/1/2010-1 | UDate:12/31/2009 |
date | .first | the first day of the month | UDate:6/12/2005.first | UDate:6/1/2005 |
date | .last | the last day of the month | UDate:6/12/2005.last | UDate:6/30/2005 |
date | .half | either the first or fifteenth day of the month depending on current date | UDate:11/20/08.half | UDate:11/15/08 |
date | .month | converts the date to the month in which the date resides | UDate:3/22/07.month | UMonth:3/07 |
date | .qtr | converts the date to the quarter in which the date resides | UDate:3/22/07.qtr | UQuarter:1/07 |
date | .year | converts the date to the year in which the date resides | UDate:3/22/07.year | UYear:2007 |
date | .dow | the closest date falling on the day-of-week | UDate:7/22/09.mon | UDate:7/20/09 |
date | +dow | the next date falling on the day-of-week | UDate:7/22/09+mon | UDate:7/27/09 |
date | -dow | the prior (or current) date falling on the day-of-week | UDate:7/22/09-wed | UDate:7/22/09 |
date | .month | the closest month relative to the date | UDate:8/22/09.jan | UMonth:1/2010 |
date | +month | the next month relative to the date | UDate:7/22/09+sep | UMonth:9/2009 |
date | -month | the prior (or current) month relative to the date | UDate:7/22/09-sep | UMonth:9/2008 |
month | .num | converts the date of the numth day of the month | UMonth:5/2008.10 | UDate:5/10/2008 |
month | +num | adds num to the month | UMonth:1/2010+2 | UMonth:3/2010 |
month | -num | subtracts num from the month | UMonth:1/2010-1 | UMonth:12/2009 |
month | .current | Converts the month to date within that month corresponding to today's date | UMonth:3/2008.current | UDate:3/5/08 (assuming today is fifth day of month) |
month | .first | the first month of the year | UMonth:6/2005.first | UMonth:1/2005 |
month | .last | the last month of the year | UMonth:6/2005.last | UMonth:12/2005 |
month | .half | either the June or July of the year depending on month | UMonth:11/08.half | UMonth:7/08 |
month | .qtr | converts the month to the quarter in which the month resides | UMonth:3/07.qtr | UQuarter:1/07 |
month | .year | converts the month to the year in which it resides | UMonth:3/07.year | UYear:2007 |
quarter | .num | converts the quarter of the numth quarter of the year | UQuarter:2005Q1.4 | UQuarter:2005Q4 |
quarter | +num | adds num to the quarter | UQuarter:2010Q3+2 | UQuarter:2011Q1 |
quarter | -num | subtracts num from the quarter | UQuarter:2010Q1-1 | UQuarter:2009Q4 |
quarter | .first | the first month of the quarter | UQuarter:2009Q3.first | UMonth:7/2005 |
quarter | .last | the last month of the quarter | UQuarter:2005Q4.last | UMonth:12/2005 |
quarter | .year | converts the quarter to the year in which it resides | UQuarter:2006Q2.year | UYear:2006 |
year | .num | converts to the numth month of the year | UYear:2008.10 | UMonth:10/2008 |
year | +num | adds num to the year | UYear:2010+2 | UYear:2012 |
year | -num | subtracts num from the year | UYear:2010-1 | UYear:2009 |
year | .current | Converts the year to month within that month corresponding to today's date | UYear:2008.current | UMonth:5/2008 (assuming it is May) |
year | .first | the first month of the year | UYear:2005.first | UMonth:1/2005 |
year | .last | the last month of the year | UYear:2005.last | UMonth:12/2005 |
year | .Qn | converts the year to the quarter | UYear:2007.Q3 | UQuarter:2007Q3 |
Below are some examples of date calculations.
UDate:current-jan.1..current | the date range from the beginning of the year to today | |
Udate:current.month.half.1..current.month.half+6.1-1 | from the first day of the current half of the year to the last day | |
Udate:current-mon..current-mon+6 | the current week from Monday through Sunday | |
UYear:current.11.7-thu+21 | the fourth Thursday of November - Thanksgiving |
The UV4 dimension is a dimension reserved for triggering special V4 actions. Various exceptions, failures, and initializations are handled within V4 by evaluating an intersection containing a UV4 point. For example if an intersection evaluation fails then a behind-the-scenes evaluation of [UV4:IsctFail] may take place. If this evaluation succeeds then the result is returned as if it were the result of the original triggering evaluation.
When the V4 parser rejects a dimension point (for whatever reason but usually invalid syntax), it creates an intersection of the form [UV4:AcceptorFail Dim:dimension Alpha:pointspecification] and evaluates it. If the evaluation succeeds then the value is is used as the point value.
[UV4:AcceptorFail Dim:UDate Alpha..] UDate:{now} | Use today's date on any erroneously specified dates |
The declared point type of a dimension usually defines the syntax of points on that dimension. The use of the Acceptor attribute in the declaration of a dimension along with a UV4:Acceptor binding enables the programmer to define a custom acceptor for dimension points.
When a dimension is declared with the Acceptor attribute, then the V4 parser creates an intersection of the form [UV4:Acceptor Dim:dimension Alpha:pointvalue] and evaluates it. The evaluation should return a point of the same dimension as dimension (although V4 does not enforce this).
If a dimension is declared with the ADPoint attribute then a slightly different intersection is evaluated. In this case [UV4:Acceptor adpoint Alpha:pointvalue] where adpoint is the point given for the ADPoint attribute.
If a point specification begins with the "#" character then this Acceptor processing is disabled and the value following the "#" is taken, as is, as the point value.
The example below maps Branch:Test to Branch:#1, Branch:Sales to Branch:#1,#2,#5, and all others by attempting to match the point value to one of the values of Branch.Id.
Example 1 - A Dimension Acceptor Example
Dim Branch Int Acceptor [UV4:Acceptor Dim:Branch Alpha..] TEQ(BrId:=Alpha* BrId:Test Branch:#1 BrId:Sales Branch:#1,#2,#5 Enum(Branch.. If::{Branch.Id = BrId:=Alpha*} Then::Branch*),Branch:#0) |
Example 2 - Multiple Dimensions Using Same Acceptor Via ADPoint
Dim OEPromo Int Acceptor ADPoint ETbl:70 Multiple Dim OESource Int Acceptor ADPoint ETbl:41 Multiple Dim OEType Int Acceptor ADPoint ETbl:174 Multiple Dim Office Int Acceptor ADPoint ETbl:133 Multiple [UV4:Acceptor ETbl.. Alpha..] Enum(ETbl.Entries If::{ETblE.Input = Str(Alpha* UC?)} Then::ETblE.ETblIV), EchoE("No entry (" Alpha* ") in Table #" Str(ETbl*) " - " ETbl.Name) |
The UV4:CodedRange point is used to initialize a dimension's coded range table. It is invoked within the In module as [UV4:CodedRange dimension] and would most likely result in a call to the V4 module to initialize then range.
Dim ProgId CodedRange DotDotToList Acceptor Displayer Multiple | Defines CodedRange dimension | |
[UV4:CodedRange Dim:ProgId] V4(Dim:ProgId Number::200500 Range::200501..200509) | Defines the number 200500 to include the range 200501..20509 | |
{ProgId:#200503 == ProgId:#200500} | results in Logical:True | |
{ProgId:#200534 == ProgId:#200500} | Logical:False |
The Set MacroSave and Set TableSave commands tell V4 to save the source code definitions. The definitions are store under bindings of the form [UMacro:macro UV4:Definition] and [UTable:table UV4:Definition].
[UMacro:Demo UV4:Definition] | returns the definition of the Demo macro |
When V4 parses a dimension reference to a non-existent dimension it attempts to map that dimension reference to an existing dimension by creating an intersection of the form [UV4:DimSynonym NId:dimreference] and evaluating it. If the evaluation succeeds and returns a point on the Dim dimension then that dimension is used. This allows the designer to create any number of synonyms for a dimension.
[UV4:DimSynonym Div] Dim:Division | Creates the synonym "Div" for the "Division" dimension |
The UV4:Displayer point is used, along with the Displayer attribute in the Dimension declaration, to control the output formatting of a dimension's points. It is often used in conjunction with the Acceptor attribute and UV4 point. When a dimension point is to be displayed, V4 evaluates the intersection [UV4:Displayer dimension..] and output the result of the evaluation.
Note: This output formatting is disabled when the point is output as part of a trace or debug message by the V4 runtime. The Str module can be used to force formatting via this convention.
The example below demostrates a displayer for the Branch dimension. A Branch point is displayed as its corresponding Branch.Id value.
Example 3 - An Example of a Dimension Displayer
Dim Branch Int Displayer [UV4:Displayer Branch..] Is1(Branch* Branch.Id,BrId:UNK MakeP(Dim:BrId `EnumCL(Branch* @Branch.Id,BrId:UNK))) |
The UV4:DisplayerTrace point is used is similar to the UV4:Displayer except that it is used by V4 to display point values when a context point is displayed (error traces, Context Examine dumps). This option may be used for special formatting of a point within a trace to make it easier to read and/or debug. It may also be used to inhibit or alter the value of a point within the context (such as a credit card number) that you do not want to inadvertantly display.
The dim.. construct is useful for referencing all points on a dimension. In some cases, however, the points on a dimension are not known ahead of time. The UV4:DotDotToList point can be used to trigger an evaluation which then returns a list calculated at that instant in time. Dimensions that refer to external databases should use this feature to determine, at run time, its points.
Note: The dimension must be declared with the DotDotToList attribute for this function.
Dim UYear Local DotDotToList | This attribute must be defined for the dimension | |
[UV4:DotDotToList Dim:UYear] Transform(UYear:{now} First::{UYear* - 3} Last::{UYear* + 3}) | Defines UYear.. to be the 3 prior years through the three following years |
The UV4:EvalStream point is the name given to the output stream when redirection to a file is specified with the Evaluate command. This can be used when multiple output streams have been created and this stream is no longer the default.
The UV4:IsctFail point is used to "trap" evaluation failures and generate a point that is to be used as the result of the failed evaluation. It is most useful in providing values for missing database points. The UV4:IsctFail point is removed from the context before any nested processing occurs.
It is extremely easy to be burned by this feature. Understanding its operation is important. When an evaluation fails, the V4 interpreter checks to see if the UV4:IsctFail point has been defined. If not then the evaluation continues as a failure. If the UV4:IsctFail point is defined then all of the points of the failed intersection are added to a new context frame and then [UV4:IsctFail] is evaluated. If it returns a result then it is used as the value of the failed evaluation. If an intersection is followed by a ,point then this processing does not occur unless the following point also fails.
[UV4:IsctFail Cus.. Id] Str("?CusId(" Cus* ")?") | Defines a value if Cus.Id fails |
This point is used in the evaluation of EvalAE expressions. Tokens appearing within the module are evaluated via [UV4:EvalAE token].
This point is used in the validation of EvalAE components. Points appearing within the module are evaluated via [UV4:EvalAEVal point].
This point is used in the validation of EvalLE components. Points appearing within the module are evaluated via [UV4:EvalLEVal point].
The UV4:TableDefine point is used to define a V4 table at run-time if the table is not already defined. Whenever a table is referenced (ex: the Include command) and it is not currently defined then V4 will evaluate [UV4:TableDefine name] where name is the currently undefined table. If the evaluation results in the definition of the table then V4 continues. If the table is still undefined then an error is generated.
dim* | Represents the point on dimension dim in the current context. |
dim** | Represents the point on dimension dim in the prior frame in the current context. |
dim.. | Represents all points on the dimension. |
dim:value | Represents a particular dimension point. The syntax of value depends on the declaration of the dimension. |
:value | A particular dimension point where the dimension is taken from the prior point. |
dim:{Context} | References the current value of the dimension in the context (same as dim*). |
dim:{Now} | References the point on the dimension corresponding to 'now'. This is only valid for time related points. |
dim:{New} | Creates a new point on the dimension. |
dim+ | same as dim:{New}. |
dim:{Sample} | Creates a sample point for the dimension. |
dim:{Undefined} | Creates an “undefined” point for the dimension. This point may be inserted into the context to effectively remove a point from the context. |
dim~ | same as dim:{Undefined}. |
~point | Only valid within a binding intersection. Specifies that the dimension of point is to be removed from the context after successfully matching the binding and evaluating the value. |
dim:<value | All points of the dimension less than value. |
dim:<=value | All points of the dimension less than or equal to value. |
dim:>=value | All points of the dimension greater than or equal to value. |
dim:>value | All points of the dimension greater than value. |
dim:<>value | All points of the dimension not equal to value. |
dim:p1..p2 | Denotes points p1 through p2 on the dimension (example Int:1..10) |
dim:p1,p2 | Denotes points p1 and p2 on the dimension (example Color:Red,Blue) |
dim.point | Is equivalent to the intersection [dim* point]. More than one point is allowed in this construction. “Customer.SalesRep.Manager.Name” reads “the name of the manager of the salesrep for the current customer and expands out to [Name [Manager [SalesRep Customer*]]]. |
.point | Assumes dim.point where dim is the dimension of the prior dim.point construction. |
intersection,point | If the evaluation of the intersection fails then use point as the value. Multiple points can be given. [Sales Customer* Month:99-Jan],{[Sales Customer Year:1999] / 12},0 first attempts to evaluate the sales for the current customer for January 1999. If that fails then it tries to determine the sales for the entire year and divide by 12, and if that fails it results in 0. |
intersection,,point | The double comma can be used for debugging when you want to see the failure of the first intersection. With this construction, the point is ignored. |
dim:=point | Coerces point into dimension dim. This construct can be used to convert a point on one dimension to another. Obviously it is only defined in certain cases. The V4 runtime supports many arithmetic, string, and date-time conversions. For example, Month:=Date* coerces the current date to the corresponding month. |
point->dim | Coerces point into dimension dim. This construct is similar to the above but can be followed with a period ('.') and additional points (ex: a.b->c.d). |
intersection=value | The binding of an intersection to a value point. |
name[. . .] | This is an array reference identical to Array([name] . . .) |
name[Dim:dim . . .] | An array declaration identical to BindQE([name] Array(Dim:dim . . .)) |
@point | A quoted point. Normally, the V4 interpreter evaluates each argument point of a module. Quoted points are not evaluated. Note that modules ending in ‘Q’ imply the quoting of one or more arguments. |
`point | Force the evaluation of the point When this is a module argument and the result is a list then each point in the list is considered to be a separate argument to the module. |
intersection* | The evaluation of an intersection which is then inserted into the context. |
point1/point2 | Equivalent to EnumCL(point2 @ point1). The point Maximum([Salary]/Emp..) evaluates the salary of all employees and returns the largest. |
tag::point | A tagged point may only be used as an argument to a V4 internal module. The tag simply identifies the point as a particular type of argument. A single colon separates a dimension from its value point, two colons separate a tagged value from its point value. |
tag? | A tag value reference. A module containing a tag value reference usually returns the value specified. For example Str(stringpoint Length?) returns the length or number of characters in stringpoint. |
tag:::point | This syntax is, in most cases identical to the two-colon format. Within certain modules, a three-colon tag indicates no change if an error occurs. For example Str(string1 Before::string2) returns the characters of string1 up to the first occurance of string2. An error is generated if string2 does not appear in string1. However, with Str(string1 Before:::string2) the entire string1 is returned if string2 does not occur within it. |
{ expression } | All operations within V4 are via internal modules, there are no infix operators such as “+” or “/”. This can get confusing for most of us who are used to infix operations. The V4 interpreter will convert a traditional infix expression to prefix form when the expression is enclosed in braces. For example {Rep.Sales * [Commission]} is converted to Mult(Rep.Sales [Commission]) |
module(arg1 … argn) | A module reference. |