Announcement

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

  • Can a program recognizes a previously defined local?

    Hi all, easy (but tricky for me) question: I have defined a local in my code and at some point I introduce a program, where I would need to include the local without defining it again. I am aware that the local, by definition, is local, but can I do that? (without relying on a global) Thanks.

    The reason is quite simple: I control all the locals at the beginning of the code and, when I need to change the value of this specific local, I do not want to go to different lines.

  • #2
    No, you can't do that. Local is local. The program cannot access the local that is defined outside it, and even if you re-define a local with the same name inside the program, that change will evaporate once control exits the program back to where it is called from.

    What you can do is add an option to your program that receives the value of the local, then you can calculate the modified value you want the local to have inside the program, and have the program return that calculated value. Then in the code immediately after the program returns, you can reset the value of the local macro to the returned value. Something like this:

    Code:
    local my_local whatever
    
    ...
    
    program define my_program, rclass
        syntax ..., local_value(string)...
        ...
        scalar new_local_value = ....
        return scalar new_local_value
        ...
        exit
    end
    ...
    my_program, local_value(`my_local')
    local my_local = r(new_local_value)
    Since this is a bit cumbersome, I would encourage you to rethink whether you really need to embed the calculation of the changed value for the local in a program.

    Comment


    • #3
      Thanks a lot Clyde. I follow your suggestion and avoid doing that. Would you suggest using a global?

      Comment


      • #4
        No, I would not recommend using a global. Global macros are an unsafe programming practice that should only be used when there is no alternative. The alternative I suggested in #2 is a little inconvenient, but it is a viable alternative. The problem with using a global is that there could be some other program (perhaps something you aren't aware of that is called in the course of executing some other command in your program) that also uses a global macro of the same name and messes with it. This produces the most intractable debugging problems imaginable. Your global is "magically" changing for no reason you can find in your code--to discover the source you have to go digging into the code of all the commands you use. Since StataCorp makes sparing use of globals in its code, and most of the people who contribute programs at SSC or SJ also do so, this problem doesn't arise often. But I can tell you from painful experience (not in Stata but a similar situation in another programming language) that if it happens to you once, you will never, never want to have it happen to you again.

        I have been using Stata since 1994, and in that time I have only felt forced to use a global macro once. Looking back on that experience, I recognize even that one use as a mistake: I could and should have stored that piece of information as a characteristic of the data set instead.

        Comment


        • #5
          Message delivered! Thanks for sharing.

          Comment


          • #6
            Originally posted by Clyde Schechter View Post
            No, you can't do that. Local is local.
            Technically, you can do that. But you should arguably really know what you are doing, and I consider discovering the relevant (non-documented) commands for yourself a necessary first step in knowing what you are doing. I think Clyde gives good advice here.

            Comment


            • #7
              Originally posted by Alessandro Franconi View Post
              I have defined a local in my code and at some point I introduce a program, where I would need to include the local without defining it again.
              If this implies a setup such as

              Code:
              local foo bar
              [...]
              capture program drop my_program
              program my_program
                  code_referring_to `foo'
              end
              then there is probably no need to do anything. That is, if the program is (re-)defined in the same do-file in which the local is defined, (re-)running the do-file will ensure that `foo' inside the program evaluates to whatever contents you put into local foo at the beginning of that do-file.

              Comment


              • #8
                Hi Daniel, thank you for your help. I am not sure I understood though. The code I run is self-contained in a unique do-file, and is something like the following
                Code:
                local foo bar
                [...]
                
                capture program drop my_program
                program define my_program
                
                local foo bar
                [...]
                
                exit
                end
                What exactly are you suggesting to do?

                Comment


                • #9
                  Perhaps I am misunderstanding daniel klein or Alessandro Franconi But as I understand #1, the desire is to do something like:

                  Code:
                  clear*
                  
                  local foo bar
                  
                  program define my_program
                      display `"Foo is `foo'"'
                      local foo bar_bar
                      exit
                  end
                  
                  my_program
                  
                  display "Revised Foo is `foo'"
                  But this does not work as intended:

                  Code:
                  . clear*
                  
                  .
                  . local foo bar
                  
                  .
                  . program define my_program
                    1.     display `"Foo is `foo'"'
                    2.     local foo bar_bar
                    3.     exit
                    4. end
                  
                  .
                  . my_program
                  Foo is
                  
                  .
                  . display "Revised Foo is `foo'"
                  Revised Foo is bar
                  
                  .

                  Comment


                  • #10
                    Originally posted by Clyde Schechter View Post
                    Perhaps I am misunderstanding daniel klein
                    The misunderstanding is on my part. More precisely, after stating how you should know what you are doing, my claim in #7 turns out to be plain wrong. I have been coding too much in Mata lately, where you can naturally define locals before the code compiles. Stata does not compile code in that way, which Clyde's example demonstrates.

                    FWIW, here is what I was thinking of

                    Code:
                    clear all
                    
                    local foo bar
                    
                    mata :
                    
                    void my_function()
                    {
                        printf("Foo is `foo'\n")
                    }
                    
                    end
                    
                    mata : my_function()
                    which works as intended

                    Code:
                    . clear all
                    
                    .
                    . local foo bar
                    
                    .
                    . mata :
                    ------------------------------------------------- mata (type end to exit) -----------------------------------------------------------------------------------------------------------------------
                    :
                    : void my_function()
                    > {
                    >     printf("Foo is `foo'\n")
                    > }
                    
                    :
                    : end
                    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                    
                    .
                    . mata : my_function()
                    Foo is bar
                    There are a few situations where you want that. You would usually code something like this:

                    Code:
                    clear all
                    
                    local foo bar
                    
                    mata :
                    
                    void my_function()
                    {
                        printf("Foo is %s\n", st_local("foo"))
                        st_local("foo", "foo_bar")
                    }
                    
                    end
                    
                    mata : my_function()
                    
                    display "Revised Foo is `foo'"
                    or, even better, pass the local macro name as an argument. Anyway, the above yields

                    Code:
                    . clear all
                    
                    .
                    . local foo bar
                    
                    .
                    . mata :
                    ------------------------------------------------- mata (type end to exit) -----------------------------------------------------------------------------------------------------------------------
                    :
                    : void my_function()
                    > {
                    >     printf("Foo is %s\n", st_local("foo"))
                    >     st_local("foo", "foo_bar")
                    > }
                    
                    :
                    : end
                    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                    
                    .
                    . mata : my_function()
                    Foo is bar
                    
                    .
                    . display "Revised Foo is `foo'"
                    Revised Foo is foo_bar

                    I think I have caused more confusion than necessary. Sorry, perhaps I should just have stayed out of this one.
                    Last edited by daniel klein; 02 Mar 2022, 01:56.

                    Comment


                    • #11
                      I think I have caused more confusion than necessary. Sorry, perhaps I should just have stayed out of this one.
                      Daniel, no harm no foul! (thanks to Clyde)
                      Last edited by Alessandro Franconi; 02 Mar 2022, 03:16.

                      Comment

                      Working...
                      X