Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • avoid braces around global macros in forvalues range

    I have always been believing that using {} around global macros is a good practice until I encountered this issue. I feel this is a bug that Stata should correct.

    An example: running

    global numcohort 3
    global numperiod 2

    forvalues g = 1(1)${numcohort}{
    forvalues t = 1(1)${numperiod} {
    di "text"
    }
    }


    would lead to the error message program error: code follows on the same line as open brace

    Dropping the {} around numcohort will make the codes work. My guess is that the outmost forvalues interpret the first { it detects as the beginning of the commands for loop. Interestingly, the braces around numperiod does not cause any problem. This inconsistency is why I think the issue is a bug. But I am no expert in developing programing.

  • #2
    The described behavior indeed contradicts documented behavior in [P] macro:
    To substitute the macro contents of a global macro name, the macro name is typed (punctuated)
    with a dollar sign ($) in front. To substitute the macro contents of a local macro name, the macro name
    is typed (punctuated) with surrounding left and right single quotes (`'). In either case, braces ({ })
    can be used to clarify meaning and to form nested constructions.
    [added in edit]
    and also in [U]18.3.10 Advanced global macro manipulation

    At the very least, the documentation seems incomplete.

    While changing the documentation would remove inconsistencies, I still dare ask StataCorp whether the behavior in #1 is intended and if so, why? If it is not intended behavior, it is a bug that should be fixed.


    Added:
    The
    Last edited by daniel klein; 24 Jul 2024, 01:32.

    Comment


    • #3
      Whether the use of braces { } as in #1 is good practice is in turn a fair question. But it's not necessary. The only essential use of braces with global macros is to make clear where the macro name ends when it's used a little ambiguously. That is if you want (say)

      Code:
      $foobar
      to be read as

      Code:
      ${foo}bar
      If this is a bug, it has taken since Stata 7 when forvalues was introduced to come to light.

      I don't have a view for StataCorp, but if this hasn't obviously bitten before, it's because Stata code does not often include examples like those in #1.

      Comment


      • #4
        Originally posted by Nick Cox View Post
        If this is a bug, it has taken since Stata 7 when forvalues was introduced to come to light.
        Assuming this is the first (public) mention, yes. I find it more likely that the bug has been noticed before but nobody cared to report it mainly because the workaround is straightforward. That does not change the fact that the behavior might be unintended and clearly contradicts documentation.

        I do agree that the code in #1 is probably seldom used if only because the use of global macros (in general and) in this context is questionable practice in the first place.

        Comment


        • #5
          However, I would be interested to know why the bug only occurs in the first forvalues ​​loop and not in the second as both look identical to me.

          Comment


          • #6
            What is referring to in #5 is, I believe, a variation of the code in #1 along the lines
            Code:
            global numcohort 3
            global numperiod 2
            
            local numcohort ${numcohort} // <- new
            
            forvalues g = 1(1)`numcohort'{ // <- changed to local macro
            forvalues t = 1(1)${numperiod} {
            di "text"
            }
            }
            The above code runs without problems.

            Obviously, I cannot access the internals of forvalues so I have to guess what's happening. I guess the problem arises because the syntax parser parses the braces before macro substitution; otherwise, the braces would have already been gone. It appears that the syntax parser acts this way in do-files (and in the command line), perhaps to speed things up (otherwise you would need at least two passes: a first pass to substitute macros and a second pass to execute the code with substituted macros in place). I further believe that forvalues (and other commands that implement the braces notation*) somehow create a separate namespace, similar to a program. In fact, we can get the code to work within a program, too.
            Code:
            program forvalues_global_with_braces
            global numcohort 3
            global numperiod 2
            
            forvalues g = 1(1)${numcohort}{
            forvalues t = 1(1)${numperiod} {
            di "text"
            }
            }
            end
            will not throw an error. I am still guessing here but it appears that separate namespaces, such as programs, are (necessarily) passed twice: once for setup (e.g., define the program), and once for execution. Note that a regular program would not call itself (as forvalues seems to do); you would need to initiate the second pass by explicitly calling the program. Anyway, I guess the syntax parser interprets things differently during the first pass that sets up the namespace (or program). With lots of details left implicit, I believe this is why the inner loop works.


            * I am wrong. Prefix commands, such as quietly, nobreak, etc. do not seem to work like forvalues. I cannot get the code in #1 to run simply by surrounding it with one the mentioned prefixes.
            Last edited by daniel klein; 24 Jul 2024, 05:30.

            Comment


            • #7
              Reflecting on my assumptions about the differences between do-files (and command line) and programs (or similar environments) in #6, I now believe that the described behavior in #1 is an unintended side-effect of an intended behavior of the syntax parser. As such, it's not necessarily a bug. Still, documentation should probably mention this exception.

              Comment

              Working...
              X