Side Effects of Quit and Return Commands with $Increment, or "I.E.Repin. Unexpected..."

In recent discussion on CachéQuality I was (friendly) blamed for old syntax promotion and deliberate obfuscation of the code. Therefore I decided to clarify my point and shed some light on one of possible source of side effects that may unexpectedly occur with RETURN command with an argument.

Disclaimer. While the specific of RETURN command is not discussed in this text, it can be replaced with QUIT everywhere without loosing the meaning.

Honestly, there are not so many constructs in ObjectScript prone to side effects. One of them is built-in $increment function. As everybody knows, it performs an atomic operation on its arguments, not only returning the calculated sum, but also affecting the first argument. E.g.

 USER> set a=1 set b=$increment(a,2) zwrite a,b
 a=3
 b=3

The default value for the second argument is 1. Further details on $increment can be found in docs.

Now, revenons à nos moutons. After restyling, my sample from CachéQuality discussion looks like this: 

/// write ##class(z.Scratch).t1()
ClassMethod t1() As %Integer [ ProcedureBlock = 1 ]
{
  do ..t2(.a,.b)
  do ..t3(.a,.b)
  return $increment(a,-1)*$increment(b,-1)
}

ClassMethod t2(ByRef pA, ByRef pB) As %Integer [ ProcedureBlock = 1 ]
{
  set pA=2, pB=3
  return pA*pB
}

ClassMethod t3(ByRef pA, ByRef pB) As %Integer [ ProcedureBlock = 1 ]
{
  set pA=4, pB=5
  return $increment(pA)*$increment(pB)
}

Let's try to guess the return value of method t1().

First of all, method t2() sets private variables a and b (passed by reference using ".var" syntax) to 2 and 3 respectively. Returned value of t2() is discarded by caller as t2() is called using DO command rather than using "SET x=..t2(.a,.b) ". Off with it.

Secondly, method t3() does the similar job, setting the same variables to 4 and 5. But on return it calculates the product of their $incremented values. Again, the returned value is discarded by caller.

At last, t1() returns $increment(a,-1)*$increment(b,-1).

What value will it return? One can expect that as $increment(pA)*$increment(pB) result is dropped, the calculation does not occur at all, so 3*4=12 will be returned, and will be wrong! In reality, the RETURN command argument comprises the expression $increment(pA)*$increment(pB) is calculated anyway, therefore both variables are incremented, so a=5 and b=6 on t3() exit. That's why RETURNing $increment(a,-1)*$increment(b,-1) we get 
(5-1)*(6-1)=20.

To be honest, these species of RETURN command are well documented. To avoid possible side effects of that kind, we could modify the last line of t3(), getting the following:

ClassMethod t3(ByRef pA, ByRef pB) As %Integer [ ProcedureBlock = 1 ]
{
  set pA=4, pB=5
  return:$quit $increment(pA)*$increment(pB) return
}

By doing so, we introduce the check up of calling sequence type: as a function ($QUIT=1) or not ($QUIT=0), calculating the result in the first case only. Our case is second one, so a and b would keep 4 and 5 values on return, and t1() would return (4-1)*(5-1)=12. Whether it is desirable, depends on algorithm used. My sample looks rather silly, but its purpose was no more than demonstrate RETURN + $increment possible combined side effect.

Hope this help somebody.

P.S. Ilya Repin's "Unexpected [Visitor]" famous painting image can be found here.

 

 

 

  • 0
  • 0
  • 198
  • 4

Comments

Let's try to guess the return value of method t1().

Is that supposed to be hard? I immediately visually determined the result 20.

To be honest I don't really understand the point which the author wanted to convey. Maybe it's the lack of English.

In this case, the t3 method code is equivalent to the following code:

ClassMethod t3(ByRef pAByRef pB) [ ProcedureBlock = 1 ]
{
  set pA=4, pB=5
  set x=$increment(pA)*$increment(pB)
}

PS: by the way, absolutely nothing will change fundamentally if you replace "return" with "quit" (and "ByRef" with "Output").

Is that supposed to be hard? I immediately visually determined the result 20

Vitaliy, you are apparently not a beginner.

the t3 method code is equivalent to the following code

No, because my version can return a non-empty value having been called as a function, while yours can't.

absolutely nothing will change fundamentally if you replace "return" with "quit"

Absolutely agree, and that was my initial point already published in recent discussion:

  • If "QUIT" was used correctly, and one replaced it with "RETURN", no miracle will happen with code readability.
  • In contrast, forgetting or being unaware of possible side effects can make code understanding harder.

The sample was inspired by those thoughts. Its initial version was a bit more tricky, but InterSystems discourage an "old syntax" even to be used in discussions, while IMHO even a beginner should be aware of it and its caveats.

As to RETURN, it seems that InterSystems promotes this command nowadays as more visually clear remedy to exit methods. Before its addition to Caché docs mentioned "Implicit QUIT", and now it tell us about "Implicit RETURN", while "Implicit QUIT" is still around. 

So it could be named "Side effects of Quit and Return command with $Increment", right?

As to RETURN, it seems that InterSystems promotes this command nowadays as a more visually clear remedy to exit methods.

We want to promote things which help.

It seems to me that having "return" in ObjectScript we can change the meaning and usage of "quit" as for cycles interruption only?

Not touching "dot syntax" and argumentless "quit" in for what are the cases of using "quit" we have?

Not touching "dot syntax" and argumentless "quit" in for what are the cases of using "quit" we have?

As to docs, "RETURN and QUIT differ when issued from within a FORDO WHILE, or WHILE flow-of-control structure, or a TRY or CATCH block." Besides these special cases, both commands work quite similar.

I didn't want to start discussing "RETURN vs QUIT", but if you insist... 

RETURN Pros:

  • visually clear sign of return from method/$$-function/do-subroutine call,
  • syntactically the same commands exist in many other programming languages,
  • due to ability to exit from any context without restrictions, RETURN can be accepted as a powerful feature for "alarm exit" on error.

RETURN Cons:

  • As to "alarm exit": QUIT combined with outer try / catch block does the same job (and even can do more of it).
  • It came too late; adding a new feature to the language which was kept unchanged for ages should have stronger reasons than it has (IMHO).
  • While substitution of QUITs with RETURNs in all appropriate places is not an easy task, the project code base may get a mixture of legal (and "legacy") QUITs and newer RETURNs, introduced by newcomers from other languages. I doubt if it would improve readability. BTW, to my surprise, I noticed RETURNs in one junior's class method, and never met it in middles' or seniors' code.
  • It can be important for some companies to support backward compatibility of their products with older and/or other versions of database management systems they use because it's not always possible to upgrade the customers to modern Caché / IRIS versions.
  • (funny one) Lazy developer has no option to use single-character shortcut for RETURN command. Three character word for one command is too long... wink