Local Conditional Assignment

75 views
Skip to first unread message

Michael DeBellevue

unread,
Aug 15, 2023, 3:20:55 PM8/15/23
to Macaulay2
What's the right way to have local variables in conditional statements within a function?   Through trial and error I've found three ways of doing things that (I think) are equivalent in terms of their outcome, although one creates a warning message.  I'm curious what other people do and if there are any conventions.  I know the distinction is a bit silly, but I don't know if there are any functional differences between the approaches and I'm also curious about what's going on under the hood.

Say I have a function which has a local parameter depending on the input value, something like the following:

```warningMessage = i -> (
    if i == 1 then (
v := 2;
return v*i
)
    else (
v := 3;
return v*i
    )
)
```
This causes the message "warning: local declaration of v shields variable with the same name" to be generated at the line v:=3.  This is because when the code is interpreted, the interpreter initializes v as a local variable when it encounters the first instance of v := 2, and then perceives v := 3 as a second initialization of v as a local variable within the same scope.   In other words, for the function

```warningMessageSupressed = i -> (
    if i == 1 then (
x := 2;
return x*i
)
    else (
x = 3;
return x*i
    )
)
```

no warning is generated, and x is not assigned a value accessible outside of the function's scope, even if I only call warningMessageSupressed(2).  Furthermore, the code

```nullValue = i -> (
    if i == 1 then (
y := 2;
return y*i
)
    else (
return y*i
    )
)
```

returns the error "no method for binary operator * applied to null and ZZ" instead of "...applied to a symbol and ZZ" indicating that y has been initialized as a local variable, even though it hasn't been assigned a value.

Another way to eliminate this message is something like the following:

```
initializeLocal = i -> (
    w := w;
    if i == 1 then (
w = 2;
return w*i
)
    else (
w = 3;
return w*i
    )
)
```
To me, this is better than the syntax in warningMessageSupressed, since it is clear that w is local to the function, regardless of which conditional evaluates as true.  However, the doc page on ":=" discusses this type of construction only in the context of local symbols for, e.g., creating polynomial rings local to the function, so I don't know if this syntax is considered messy/undesireable as compared to e.g. the syntax I used in the function warningMessageSupressed above.

It's not clear to me that this warning message should be generated in this circumstance.  If it is, it might be a good idea for this situation to be addressed in the documentation for the ":=" operator.

Paul Zinn-Justin

unread,
Aug 15, 2023, 9:20:17 PM8/15/23
to maca...@googlegroups.com
that's right, in Macaulay2 only functions have local symbols, not statements in ( ... )
so your warningMessageSupressed solution is the correct one, though I also find it a bit ugly.
another option similar to your last one is:

initializeLocal = i -> (
    local w;

    if i == 1 then (
    w = 2;
    return w*i
    )
    else (
    w = 3;
    return w*i
    )
)
--
You received this message because you are subscribed to the Google Groups "Macaulay2" group.
To unsubscribe from this group and stop receiving emails from it, send an email to macaulay2+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/macaulay2/03a35124-5725-4d07-8b6a-8079d4c47e05n%40googlegroups.com.

Michael DeBellevue

unread,
Aug 22, 2023, 5:15:24 PM8/22/23
to Macaulay2
Paul,

Thanks!  Your example looks like what it's "supposed to be", so I appreciate you providing that.

I don't think only functions have local symbols though: the following function does not produce the warning:

```
forLoops = i -> (
    j:=2;
    for i to 3 do (
        j:=3;
        print(i);
    )
)
```

I'm not sure where in the documentation to find a comprehensive description of what generates a new scope, or how to determine the scope of a chunk of code.
If anyone else is more curious about this, it looks like the relevant line is 561 of binding.d.

Thanks again!

Michael

Paul Zinn-Justin

unread,
Aug 22, 2023, 7:44:32 PM8/22/23
to maca...@googlegroups.com
you're right, "for" loops also seem to create local scope; from the documentation:
"The variable i is a new local variable whose scope includes only the expressions px, and z in the body of the loop. Moreover, new local variables defined inside the body of the loop will not be visible outside it."
and yes, it'd be nice to have somewhere centralized in the documentation a list of situations where local scopes are created.

Paul Zinn-Justin

unread,
Aug 22, 2023, 7:58:54 PM8/22/23
to maca...@googlegroups.com
PS. incidentally, in the web interface, you can do this:

Screenshot from 2023-08-23 09-57-56.png

Michael DeBellevue

unread,
Aug 24, 2023, 4:53:01 PM8/24/23
to Macaulay2
Neat!  Similar output is generated on local installations when you run "disassemble" on an object of type pseudocode, so I think the web interface is just printing that output in a sensible way.  Not sure why this doesn't happen locally.

Paul Zinn-Justin

unread,
Aug 24, 2023, 8:01:38 PM8/24/23
to maca...@googlegroups.com
yes, the web interface version of Macaulay2 is ahead of the official version by a few commits that I haven't had time to PR yet, including a revamping of the pseudo code.

Reply all
Reply to author
Forward
0 new messages