Discussion
· Jan 24, 2018

For and If with one line: to brace or not to brace

Hi Community!

Coding guidelines discussion. Consider you have For with one command in the cycle. Here are the options:

One

For i=1:1:1000 set ^Test(i)=""

Two

For i=1:1:1000 {
 set ^Test(i)=""
}

Same for the "If":

if a set b=a

or

if a {
 set b=a
}

Your choice? Or maybe even other options? 

Discussion (58)12
Log in or sign up to continue

I tend to use "if" rather than postconditionals because it makes the code read more like spoken language, and I think it's easier to understand for those who are not seasoned Caché veterans.

However I make exceptions for "quit" and "continue":

quit:$$$ISERR(tSC)

continue:x'=y

I like to have these out in front, rather than buried in a line somewhere, so that when I'm scanning code I can easily see "this is where the execution will branch if a certain condition is met."

Yep, brace always.

Because you will never know if you need to update your condition or chain another. The brace shows what is the scope of the condition, also for loops, for, for each, etc.

It is a good practices and it is more readable.

As Eduard Lebedyuk   has commented, a single IF for use to set a variable, the postconditionals is a good idea, coz you know the condition before to set.

Danny and Robert, of course I understand what you are saying (we are the same generation!). Also, I am not trying to be the Developer Community police.

In posts that are asking "what's the recommended way to do this?" we should answer that question. There's no point in mentioning old syntax in that context. Saying "don't do this, but you might see it someday" helps the subset of people that might work with old code, but doesn't help and may confuse the people that are working with new code. It doesn't belong.

On the other hand, if there's a post asking "what are some older/confusing syntaxes I might encounter when working with M or ObjectScript?" we should answer that question. We all have examples of that.

Everyday I read legacy code like this, but if we're talking about the ideal (like coding guidelines) we want to avoid these. I agree that every Object Script developer should be able to read this code, but they should also know that there are better modern ways to write this. 

I think that part of growing the community of Object Script developers is to lower barriers to entry. Having worked with a lot of new Object Script developers (and been one myself), one of the largest barriers to entry is the prevalence of cryptic legacy syntax in existing code. 

Remember, just because we can read it doesn't mean that everyone can.

I always wanted to extend argumentless  DO so that you could get a new scope without needing the fairly ugly dot-indentation lines.  Then you write something like

Do {
   New $roles
   Set $roles=""
   If ($extract($roles,1,$length("%All"))="%All") Set tHaveAllRole = 1
}

You could even write the whole thing on one line like

Do {New $roles  Set $roles=""  If ($extract($roles,1,$length("%All"))="%All")  Set tHaveAllRole = 1}

Well, my DO command suggestion won't work in ObjectScript because ObjectScript has extended the MUMPS DO command to accept syntax for the
   DO { code } WHILE expr,expr,...
statement.  The code block in the DO-WHILE extension does not assume the code block opens a new variable scope.

However, like most languages designed in the 1950s and 1960s, (including FORTRAN, BASIC and PL/I, but not C) MUMPS does not have reserved words.  Therefore, any newly added command can start with a new command word.  So we could have BLOCK { code } or SCOPE { code } as new commands that open a new variable scope.  We might be able to find a better command word than BLOCK or SCOPE.  We could even consider NEW { code }, although it might be considered confusing having an additional NEW command (one with a curly brace code block along with one with no arguments, one with list of argument names and one with a list of names surrounded by a pair of round braces).

You have right, do {code} and do {code} while expr are mutually exclusive.

I thought more something like this do {{code}} (no spaces between curly braces) or, as you suggested, a new keyword like WRAP {...}, of course, BLOCK {...} and SCOPE {...} are also acceptable.

By the way, we already have an alternative for argumentless DO
do {code} while 0
It looks ugly and confusing and
- does not preseve $T
- does not establich a new stack level
but one can get over both easily respective do a work-around.

We want our code to be as accessible and readable as possible to as many people as possible. We always prefer whitespace formatting and curly brackets to arcane one line syntax. Someone who's not an Object Script expert will know what this does -

if a { 
   set b=a 
}

But there's no guarantee that a novice will necessarily know what these do -

if a set b=a
set:a b=a

Good code is readable and clever one liners will get in the way of that goal.

"We want our code to be as accessible and readable as possible to as many people as possible. "
Do we? ;)
"We always prefer whitespace formatting and curly brackets to arcane one line syntax." -- I don't ;)
Using arcane one liners shows that you are a superiour wizard. One should always use the short forms of commands to make code more terse and save space. If people want to read the code, they can use 'expand code' in studio (which expands the shortform commands to the full version).
Note, there might be some sarcasm in here.
People who know me, might also know that I'm only maybe 80% joking;)

The legacy versions of If and For commands don't use curly braces. But there are no legacy versions of While and Do/While, which require curly braces. So rather than trying to remember which commands use or don't use curly braces, just use curly braces all the time for these, as well as Try/Catch.

Regarding post-conditionals, it's a good habit to put parentheses around the condition, which allows you to put spaces in the condition.

This is valid syntax: set:a=4 y=10

This is valid syntax and more readable: set:(a = 4) y = 10

This is invalid syntax: set:a = 4 y = 10

If I am modifying a routine I didn't originally write then I tend to stick to the coding style the original author adopted - even it is using legacy syntax. It is good practice not to mix old and new constructs within the same code block ie. don't mix curly braces with line-oriented syntax.  If you are completely re-writing something or writing new code then I prefer curly braces.

I would have liked to re-factor some of my code better but I found the 'use for' construct breaks when you try that. In this pseudocode, I was using the 'write' statement to write to a file on the file system. There is a condition that is checked to determine whether it gets written to file1 or file2.

// Run an extract based on arrayOfIds
for arrayIndex=1:1:arrayOfIds.Count() 
{
       // Continue to loop through array of you detect an errorStatus
      continue:errorStatus'=1
      open file1:("WNS":::):10
      // Inner-for 1
      use file1 for testIndex=1:1:testSetObj.TestCollection.Count()
       {
       }
       open file2:("WNS":::):10
       // Inner-for 2
       use file2 for testIndex=1:1:testSetObj.TestCollection.Count()
       {
       }
// end outter-for

I miss 3 things in this example:

  • There is a condition: I found no IF nor anything
  • at the end of the inner loop there is no CLOSE file1 7 /  CLOSE file2  for file1 or file2
  • OPEN file:("WNS": ......)   
    • W - Write is ok
    • S - Stream mode is OK
    • N - i s questionable. if you are not on VMS you override the file at each outer loop
      http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GIOD_rmsseqfiles#GIOD_rmsseqfiles_use

      New file. If the specified file does not exist, the system creates the file. If the specified file already exists, the system creates a new one with the same name. The status of the old file depends on which operating system you are using. On UNIX® and Windows, Caché deletes the old file. (Note that file locking should be used to prevent two concurrent processes using this parameter from overwriting the same file.)

      So to get all output you may append arrayIndex to filename to have unique file names

Thanks for the comments Robert. It's safe to assume there is an IF statement in each inner-for  to determine whether to do anything or not. Each object in arrayOfIds needs to be opened and a Boolean check is done against a property. I haven't had a problem with the 'N' parameter. The code appears to store 'write' records in process memory before writing everything to file after the outer-for. $ZSTORAGE has been buffed to accommodate this.

// Changes the max-limit on the process memory from the default 16384 Kilobytes
set $ZS=49152

Unique file names have been achieved using the following assumptions:

1) The process will run no more than once a day
2) Append today's date in ODBC format using $zd(+$h,3) to the end of the filename.

The 'close' statement appears after the outer-for but I'm not sure if the curly braces implicitly does it anyway.

// Null check. Close if necessary. (May be implicitly closed by outter-for loop).
if $GET(file1)'="" close file1
if $GET(file2)'="" close file2

Strange. Our production servers are Caché 2018 on AIX but still showing only 16,384 KB.
Must have preserved the existing setting on upgrades rather than use the new value.
Fresh local Caché install on Windows install shows 256MB though. 

%SYS>w $zv                                                            
Cache for UNIX (IBM AIX for System Power System-64) 2018.1.2 (Build 309_5) Wed Jun 12 2019 20:08:03 EDT                                                        
%SYS>w $zs
16384