Tech News Back Issues Issue: 090806

$ref and $sendallref

By David Swain
Polymath Business Systems
www.omnistraining.com
dataguru@polymath-bus-sys.com

Omnis Studio version 4.2 was released the same day I had to leave for the OmnisCentral Americas 2006 conference. The conference itself was the usual busy time for me, so there was little time to examine this new version in any detail in my "off" hours. And to answer the question that I am often asked: No, I do not receive any advanced copies of software from Raining Data, despite any publicly perceived "favored" status I might appear to have with the company. I receive the same notices as does anyone else - and my name is toward the end of the alphabet.

So I have only recently begun to explore all the fixes and features that are collectively known as version 4.2. But while it is too early to give a thorough treatment of this new version for Omnis Tech News, there is one new feature that I can discuss. This new feature, $sendallref, which fixes an important problem that we all occasionally face, was working fine in the beta version. And the existing feature that this new one enhances is one I've been meaning to visit here in Omnis Tech News for some time.

So it's time for a little notation trivia...

Using $ref as Notational Suffix

The simplest way in which the $ref notational segment can be used is as a suffix (final segment) to a notational item. Used in this way, $ref returns a reference to the notational item to which it is applied. This is useful when we wish to pass a reference to that item as a parameter of a method call or when we simply want to assign a reference to that item to an in-scope Item reference variable during the execution of the current method using the Set reference command. In fact, the .$ref is often not even necessary for these operations, although the notation is more complete and "correct" when using it. This may become more strict in the future (like using parentheses for a notational method call even if there are no parameters to pass), so it's good to get into the habit.

As an example of this use of $ref, suppose we want to use an Item reference variable to point to a list display field on a window instance. We would populate that variable like this:

Set reference listFieldRef to $cinst.$objs.listField.$ref

When used as a notational suffix in this way, $ref is treated somewhat as a property of the item to which it is applied. It also somewhat acts as a method in that it returns a reference to the item to which it is applied. But if you prefer to think of it this way, don't follow through on the "method" notation and follow $ref with parentheses (as we would a real method). Such a move would resolve the reference back to the notational item with which we began. So the notation string:

notationalitem.$ref()

both creates a reference to the notational item and then resolves that reference back to the notational item we started with. While this is a completely useless thing to do, perhaps it is instructive in some negative way.

So $ref used as a notational suffix lies somewhere between being a property and being a method. But there are other ways in which we can use this notational element...

Using $ref as Notational Prefix

More to the point of the main purpose of this article: When we use $ref as the prefix (first segment) of a notation string, which in turn is used as a parameter of a method, it acts as a reference (or as a reflexive pointer) to the notational item to which the method is applied. Notice the subtle difference between this and its use as a notational suffix. In this case, $ref pulls the notational item inside the parameter for use as an argument to be applied to itself. While this may seem a bit confusing at first, it is a very powerful feature!

For example, the $assign() method, which we can apply to any variable within Omnis Studio (including properties of items), can take advantage of $ref in this way. Within the expression that constitutes the one parameter of this method, $ref acts as a reference to the variable about to receive the value assignment. So if we have a numeric variable named numberVar1 and we wish to increment its value by 5, we can say:

Do numberVar1.$assign($ref+5)

instead of

Do numberVar1.$assign(numberVar1+5)

Each of these statements yields the same result.

For purposes of conversation, let us refer to a method that acts directly on an item as an item-specific method. That is, the method is applied to and operates upon the single, known and discrete notational item. Any item-specific method that accepts parameters can use $ref in this manner.

We can also use $ref in this way within functions and non-specific methods within the $assign() method. Consider the case where we want to concatenate additional characters with the current contents of a string variable:

Do stringVar.$assign(con('This variable contained "',$ref,'" before I clicked that button.'))

Here we have used $ref within the con() function, which itself is nested inside the parameter of the $assign() method. It is used in place of the variable name stringVar, but it refers to that variable because of the special way the statement is cast. The intermediate use of the con() function does not interfere with the reference held in $ref.

Consider one more example. Suppose that as part of a method that belongs to a field, we want to increment the variable associated with that field by 5 units. We wish to write this code generically so that we can copy the field and paste it elsewhere, change the dataname and have it work as did the original. Perhaps we even want to include it in a "special fields" collection in the Component Library. The line of code that performs the incrementation would be:

Do [$cfield.$dataname].$assign($ref+5)

Whatever the name of the associated variable, this command line will increment its value by 5 units.

But there are other kinds of method where we can use $ref in a similar way...

Using $ref with Group Methods

The $ref notational segment has a related, but subtly different meaning when it is the prefix of a notation string used within a parameter of a group method. Some group methods are applied iteratively to each member of the group in the order of their membership within that group. When used within a parameter of such a method, $ref refers to the current member of that group for the current iteration of the method.

Suppose that we want to build a list of open window instances. We want to include in this list the name, title and classname of each instance. The $makelist() method allows us to do this if we apply it to the $iwindows group. To receive the property values we desire for each window instance in the resulting list value, we must use $ref in the syntax shown here:

$iwindows.$makelist($ref.$name,$ref.$title,$ref.$class().$name)

(Notice that we need to resolve $class in the third parameter. If we do not, $name simply returns "$class".)

On execution, $ref refers to each member of the $iwindows group in its turn. We simply append to it the property, method or variable we need to work with from there.

Not all methods of a group can take advantage of $ref - only those that are applied iteratively. Some group methods, such as $next(), do not even use a parameter. Others, such as $remove(), require a notational pointer to a specific member of the group and are not applied to each member iteratively. For any notational group, there are four methods that fit these criteria: $makelist(), $appendlist(), $insertlist() and $sendall(). The first three of these are simply variations on a theme, so there are really only two processes (listing group members and messaging to group members) where $ref can be used.

My guess is that most Omnis Studio developers will use the $sendall() method much more frequently in their work than the listing methods. I know I do! This method is used to send a message to the members of a group. Its first parameter is the expression of the message. Its second parameter is a search expression that determines the scope of the message - that is, which members of the group are to receive the message. The second parameter is optional - all members receive the message if it is not used.

$ref can be used in either parameter. Consider this example:

Do $iwindows.$sendall($ref.$close(),$ref.$style=kTitle)

Here we are telling all of the window instances to close themselves if they are "title" windows. We have applied a method ($close()) and a property ($style) to $ref to make these specifications. Of course, either of these parameters could be much more complex in performing other jobs. We will discuss the implications of that on the use of $ref shortly...

Using $ref with List Methods

If you have read my books "List Variable Definitions" and "List Variable Operations" (information about these and other electronic books from the Omnis Reference Library series can be found at: www.omnistraining.com), you already understand that a list variable also acts as the notational group of lines contained within that variable. Knowing this, it follows that any method of a list variable is really a group method, so the previous section of this article applies to $search() and $sort() as well. Still, these only apply to list variables, so they are worth mentioning separately here.

The first parameter of the $search() method of a list variable contains the search specification - the expression used to select list lines. (We will ignore the other four parameters of this method for now.) $ref can again be used to represent the current line of that list as the method scans the lines of the list looking for those that meet the search criteria. The name of a column of the list is usually appended to $ref here because we most likely want to locate lines based on one or more cell values matching fixed values in some way. For example, if we want to select all lines where the quantitysold column value is greater than 5, we might use:

Do listVar.$search($ref.quantitysold>5)

We might occasionally want to select lines based on a line-by-line relationship between two column values. If we want to select all lines where quantityshipped is less than quantitysold, we could use:

Do listVar.$search($ref.quantityshipped<$ref.quantitysold)

This comparison is performed on a line-by-line basis. Using $ref is the simplest way of assuring this.

The $sort() method of a list variable requires pairs of parameters (although we can use a single parameter if only one level of sort in the default direction is required). The first parameter in the pair is the sort criterion and the second is a Boolean sort direction indicator (descending is kTrue, default is kFalse - ascending). We would normally use $ref in the odd-numbered parameters to specify which column(s) of the list we wish to use as the basis for sorting. For example, if we want a case-insensitive, two level ascending sort on last name and first name, we might use:

Do listVar.$sort(upp($ref.lastname),kFalse,upp($ref.firstname),kFalse)

We can also combine column values or extract sort values from them using more complex expressions in the odd-numbered parameters. Of course, this can affect the speed and efficiency of the sort - but that is a discussion for another time...

As a general method of any notational group, $sendall() can also be applied to a list variable. When it is, $ref refers to each line of the list in its turn. So we can only append properties, methods and cell names of a list line to $ref in this context. The first parameter of $sendall() is an expression that is evaluated for each line. This parameter is required (since not providing it would make the method somewhat pointless). The second parameter can be used to provide search criteria that specify that only certain lines trigger the evaluation of the first parameter. This parameter is optional. $ref can be used in the expression of either parameter.

For example, we might want to trigger some process of the current window instance and pass it information from the current line for each line that meets some criterion:

Do listVar.$sendall($cinst.$processlineinfo($ref.C1),$ref.C2>20)

This method passes the value of column 1 to the $processlineinfo() method of the window instance for each line where the column 2 value is greater than 20. While this is still a simple example, $ref did find its way to being a parameter of a nested method call in the first parameter of $sendall(). Fortunately for us this time, this method is not item-specific. The parameters for $sendall() can become quite complex as we learn to use it in more interesting ways. But because we can execute item-specific methods within it, we may run into the limitation to the use of $ref when we nest other methods within it. This is the problem I alluded to at the beginning of this article.

Using $ref in Nested Method Calls

While this can apply to the use of $ref within other methods, the $sendall() method is where we most commonly see this problem. Expressions within the parameters of $sendall() can be very complex and can potentially involve nested method calls five levels deep (assuming that the $sendall() itself is not nested inside any parentheses, since we still only get a total of six nested levels in Omnis Studio). In fact, it is possible that one $sendall() method can be nested inside another! This creates a problem with the use of $ref at those deeper levels if we want it to refer to the item implied by the (outer) $sendall() method. Any method that sets the $ref pointer for internal use continues to do so. With all generations of Omnis Studio before version 4.2, there was no remedy other than to put in more specific notation - and that was not always possible.

Consider this case: We want to set the value of a cell on each line of a list using $sendall(). In the first such example, suppose we just want to toggle the value of a Boolean column:

Do listVar.$sendall($ref.booleanColumn.$assign(not($ref)))

Notice that the first use of $ref here refers to the line of the current iteration, but the second use (nested within the $assign() method) refers to the specific cell to which the $assign() method is applied on each iteration. The use of this item-specific method changes the meaning of $ref for its own purposes. In this example, the change is beneficial because we want to use the current value of the cell to set its new value. But what if we want to assign a new value to a cell based on other cells on the same line?

Do listVar.$sendall($ref.sumColumn.$assign($ref.columnA+$ref.columnB))

This will definitely not work. The notation is not valid - unless sumColumn is a list, row or object that contains internal variables named columnA and columnB. But even then, a list cannot accept a numeric overall value. This column is numeric, as are its two siblings whose sum we want to put into sumColumn on each line. It could be worse because we have a workaround in this case: We could use the name of the list variable instead of $ref here. But this is definitely not always the case. This is not just a problem with $sendall() applied to a list variable. That is just a convenient example.

So what are we to do?

$sendallref to the Rescue

The engineers at Mitford House have given us a solution to this problem in Omnis Studio version 4.2 in the form of a new notational reference item - $sendallref. $sendallref maintains the reference to the current item for the current iteration of the $sendall() loop - even when nested within a parameter of an item-specific method. So we can now recast the problem above as:

Do listVar.$sendall($ref.sumColumn.$assign($sendallref.columnA+$sendallref.columnB))

This now correctly refers to other columns within the same list line as the target column of the $assign() method. The same would be true for similar situations using $sendall() on other notational groups.

$sendallref does not replace $ref in simple cases where it has always worked (like its use in the example above), but is a substitutional option. We can use the two interchangeably because $sendallref points to the same item as $ref in those cases - it just requires three times as many keystrokes to type its name. We will probably see one school of thought emerge that says to always use $sendallref for consistency with $sendall() and another (to which I would subscribe) that says use $sendallref when needed and $ref where possible to avoid the keystroke tax. Neither is right - neither is wrong.

But there is still an issue that could come up that is not resolved by $sendallref. If we have nested $sendall() methods, $sendallref is tied to the innermost $sendall() within which it is nested. That is, it works like $ref does with regard to item-specific methods, but the trigger for a pointer change for $sendallref can only be a $sendall() method. Of course, this won't come up as often...

Next Time

With those famous last words, I'm going back into version 4.2 to find a good topic for the next issue of Omnis Tech News. There are plenty of new features and significant fixes to old problems, I just have to choose one that looks like it should take us down an interesting path!

The OmnisCentral Americas 2006 conference was great! The OzOmnis 2006 conference will be super! If you couldn't make the first, try to make the other - it's less than two months away!

 
© 2006 Copyright of the text and images herein remains with the respective author. No part of this newsletter may be reproduced, transmitted, stored in a retrieval system or translated into any language in any form by any means without the written permission of the author or Raining Data.
Omnis® and Omnis Studio® are registered trademarks, and Omnis 7™ is a trademark of Raining Data UK Ltd. Other products mentioned are trademarks or registered trademarks of their corporations. All rights reserved.