$ref and $sendallref
By David Swain
Polymath Business Systems
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 Omnis Software, 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"
you may 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! |