# \`outer\` keyword to automatically \`local\` a variable?

**URL:** https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964
**Category:** Internals & Design
**Tags:** question, scope
**Created:** [November 8, 2023, 5:22pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964 "2023-11-08T17:22:40Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 8, 2023, 5:22pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/1 "2023-11-08T17:22:40Z")

</div>

When using the `outer` keyword, as desribed in [Scope of Variables · The Julia Language](https://docs.julialang.org/en/v1/manual/variables-and-scoping/#Loops-and-Comprehensions) :

```julia
julia> function f()
           i = 0
           for outer i = 1:3
               # empty
           end
           return i
       end;

julia> f()
3

```

do we really need the `i = 0` ?  
If we autmatically add an invisible

```julia
local i

```

whenever we encounter `outer i`, will it be a bad idea?

I’ve read there is some negative sentiment to the whole `outer` keyword as hinted in: ["outer" keyword, Julia for-loop variable scope - Stack Overflow](https://stackoverflow.com/questions/68180252/outer-keyword-julia-for-loop-variable-scope)

---

<div class="post-metadata">

### Author: ![rfourquet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rfourquet/32/3610_2.png) [@rfourquet](https://discourse.julialang.org/u/rfourquet)
#### Post date: [November 9, 2023, 3:39pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/2 "2023-11-09T15:39:25Z")

</div>

Note that if the “outer” `i` is not directly in the enclosing scope of `for`, adding an invisible `local i` will have different semantics, as it would create a new binding, e.g.:

```julia
julia> let i=0
           let
               for outer i=1:3 end
           end
           i
       end

3

julia> let i=0
           let
               local i
               for outer i=1:3 end
           end
           i
       end
0

```

---

<div class="post-metadata">

### Author: ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)
#### Post date: [November 9, 2023, 3:53pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/3 "2023-11-09T15:53:12Z")

</div>

So hypothetical logic for this feature request would have to be a bit more subtle: if the variable doesn’t already exist (as a local or do we also allow this to work with globals?) then you’d want to implicitly declare the variable in the immediately enclosing scope. Also, what about the case where the loop doesn’t execute? Then the existence of the untaken loop causes an undefined outer variable to be implicitly created. It seems clearer to require the user to explicitly declare the variable, then there can’t be any confusion about what scope the variable should appear in or what value it should have if the loop isn’t taken.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 9, 2023, 6:03pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/4 "2023-11-09T18:03:07Z")

</div>

> [@StefanKarpinski](#):
>
> Also, what about the case where the loop doesn’t execute? Then the existence of the untaken loop causes an undefined outer variable to be implicitly created.

That’s right, variables must be defined at compile-time unequivocally.  
However, I think an `outer` to implicitly define the `local` sounds good. Although, there is a confusion with an `outer` referring to a global, I think it is better to _always_ define an implicit `local`. And to require an explicit `global` to access the global.  
Even the keyword name `outer` seems to suggest the enclosing scope and the current default requires more effort in more cases (it isn’t the right “branch prediction”).  
Additionally, we should be wary of the simple do-nothing bias, as this is a rarer example of a not-often-used construct which could still be improved with not much cost (or maybe I’m mistaken about this. To answer this question, a source-wide grep is needed).

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 9, 2023, 10:58pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/5 "2023-11-09T22:58:22Z")

</div>

> [@Dan](#):
>
> I think it is better to _always_ define an implicit `local`

rfourquet already demonstrated that an implicit `local` statement can shadow an already existing outer local variable, so best case is doing it only when there isn’t any in the outer local scopes.

When there are no outer locals to shadow, putting one right outside the for-loop makes sense. However, as StefanKarpinski said, variable isn’t guaranteed to be initialized because the for-loop isn’t guaranteed to run e.g. `for outer i in iter` where `iter` is empty. There’s no reasonable implicit default value to fix that. Relying on an iterable to be non-empty to initialize a variable is very likely a bug, and I prefer it to be caught by a syntax error at definition before an undefined error at some of the calls:

```julia
julia> function f(n)
         local i # implicit proposal
         for outer i = 1:n end
         return i
       end;

julia> f(1)
1

julia> f(-1)
ERROR: UndefVarError: i not defined
Stacktrace:
 [1] f(n::Int64)
   @ Main ./REPL[4]:4
 [2] top-level scope
   @ REPL[5]:1

julia> function f(n)
         for outer i = 1:n end
         return i
       end;
ERROR: syntax: no outer local variable declaration exists for "for outer"
Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

```

So, the consequences of the implicit `local i` proposal is actually substantial.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 9, 2023, 11:00pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/6 "2023-11-09T23:00:54Z")

</div>

Okay… I relent… the status quo isn’t so bad.  
But still, that extra definition is quite annoying.

But there is something awkward in the way `for` loops communicate to the enclosing scope. Not knowing if the loop exited through `break` or through iterator end. Not knowing final value. Feels like these values are available to the code and to the mental model of programmer, yet not easily enough in the source code.

I invite anyone who shares this feeling to suggest an improved syntax (in this thread or somewhere else).

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 9, 2023, 11:18pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/7 "2023-11-09T23:18:01Z")

</div>

> [@Dan](#):
>
> But there is something awkward in the way `for` loops communicate to the enclosing scope. Not knowing if the loop exited through `break` or through iterator end.

You’re not alone in this opinion. The structured program theorem that moved programming languages away from GOTOs and toward block patterns does not need multiple exit points like `break` and early `return`, and some stricter interpretations enforce single exit points e.g. Pascal. However, this would require some extraneous variables and code duplication in many programs, and Shapiro found that students tend to run into bugs in Pascal that don’t happen at all if they were allowed to `return` early. Kosaraju ended up proving that you can only avoid extraneous variables entirely by allowing multi-level `break`s from a loop.

> [@Dan](#):
>
> Not knowing final value.

I mean, it’s impossible to know that at the source code in general because it’d depend on runtime values and calculations that we probably can’t do on our own.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 9, 2023, 11:24pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/8 "2023-11-09T23:24:29Z")

</div>

> [@Benny](#):
>
> it’d depend on runtime values and calculations

Of course, knowing the final value at loop exit _at run-time_. But currently it isn’t so accessible, unless you shadow it with a local or through the `outer` construct. A tad too much work, for something the programmer is quite sure the machine has access to (and similarly the point of loop exit - return or iterator-end - which can be somehow “labeled”). This use appears in so many algo pseudo code that it is a shame it’s not more syntactically simple.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 9, 2023, 11:26pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/9 "2023-11-09T23:26:24Z")

</div>

I’m sorry, I don’t understand what you mean by a value not being accessible and needing to shadow with a local. Do you have a MWE?

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 9, 2023, 11:28pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/10 "2023-11-09T23:28:30Z")

</div>

```julia
lastval = 0
for i in 20:30
   lastval = i # shadowing
   if isprime(i)
      break
   end
end

# here `lastval` is 0 if no iteration
# or last iteration before break
# (*) still can't figure if iterator-ended or broke in last iteration.

```

This kind of decision depending on loop ending condition is very common in algorithms. `outer` makes this a little simpler, but there is point `(*)` also.

I’m not suggesting any better syntax yet. But feel there should be one. And we should be couragous enough to not immediately dismiss improving this.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 9, 2023, 11:46pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/11 "2023-11-09T23:46:58Z")

</div>

So you mean that the value of `for i in` isn’t accessible from outside the for-loop unless you assign the value to another local variable from the outside? That’s normal for local variables when its origin scope ends; in this case the `i` is local to each iteration, so `lastval = i` really is necessary as an outer variable that can persist across iterations. A manual `lastval = 0` is necessary to properly initialize in case the for-loop runs 0 iterations. no changes to `for outer i in` can possibly get around that.

> [@Dan](#):
>
> `# (*) still can't figure if iterator-ended or broke in last iteration.`

You can if you store some information in extraneous outer local variables, `break` just doesn’t do it for you because it’s often unnecessary work.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [November 9, 2023, 11:58pm UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/12 "2023-11-09T23:58:01Z")

</div>

> [@Dan](#):
>
> Not knowing if the loop exited through `break` or through iterator end.

In Python this is done with `for`-`else`, where `else` means “finished without a `break`”

```julia
for x in [1,2,3]
  if p(x):
    break
else:
  print("finished normally")

```

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 10, 2023, 12:07am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/13 "2023-11-10T00:07:41Z")

</div>

> [@jar1](#):
>
> In Python this is done with `for`-`else`

Worth mentioning that this is extremely rarely used, and when someone misquoted Guido van Rossum as wanting to rename the `else` to a more intuitive name like `nobreak`, he clarified that he wouldn’t include this feature at all if he could go back in time. The flexibility and clarity of per-`break` flags and `if`-statements after the loop just outweighs it.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 10, 2023, 12:11am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/14 "2023-11-10T00:11:28Z")

</div>

How about:

```julia
rc = for i in 1:3
    if p(i)
        break :here
    elseif q(i)
        break :there
    end
end
rc == :noiter && println("empty iterator")
rc == :here && println("broke p(x)")
rc == :iterend && println("nonempty iterator ended")

```

`rc` mechanism will get optimized away if unused. Currently no return value of `for` to be backward compatible to.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 10, 2023, 12:23am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/15 "2023-11-10T00:23:27Z")

</div>

Changing for-loops and `break`s to return a value is unnecessary to accomplish that. Syntax shouldn’t be drastically modified in minor revisions even when it would be convenient.

> [@Dan](#):
>
> `rc` mechanism will get optimized away if unused.

Not possible to elide at parse-time or compile-time because the value of `p(i)` and `q(i)` across all iterations and thus whether the `break`s occur is only known at runtime.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 10, 2023, 12:31am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/16 "2023-11-10T00:31:18Z")

</div>

> [@Benny](#):
>
> Not possible to elide at parse-time or compile-time

If `rc` is not used (i.e. no `rc =` before `for`), elidable.  
This is not drastic, as this syntax is never used (`for` returns `nothing`).

> The key to change in a language is 99.9% backward compatibility,  
> not complete freeze.  
> – famous old proverb

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 10, 2023, 12:36am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/17 "2023-11-10T00:36:05Z")

</div>

> [@Dan](#):
>
> If `rc` is not used (i.e. no `rc =` before `for`), elidable.

Then that wouldn’t need to be elided or optimized away, it just wouldn’t be there in the first place.

> [@Dan](#):
>
> This is not drastic, as this syntax is never used (`for` returns nothing).

It’ll change the return values of functions whose last expression is a for-loop, so it’s not actually backwards compatible. Adding syntax isn’t always backwards compatible, it depends on implementation.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 10, 2023, 12:39am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/18 "2023-11-10T00:39:23Z")

</div>

> [@Benny](#):
>
> functions whose last expression is a for-loop

I would say this is around 99.9% backward compatible. But any upside should be measured also.  
A sample of `for` loops across the code-base and a little evaluation might help.

But I know there is no appetite for changes…

ASIDE: Function ending with `for` loops can also replace it with `foreach` which returns nothing and doesn’t need this ‘mechanism’. Of course, this is not backward-compatibility per se.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 10, 2023, 12:47am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/19 "2023-11-10T00:47:29Z")

</div>

> [@Dan](#):
>
> I would say this is around 99.9% backward compatible.

This is infeasible to prove. Sampling for-loops in source code isn’t enough because the source code does not say how often each for-loop is used, and that’ll change with the package ecosystem. A useless package can generate enough methods ending in for-loops to dwarf every other for-loop in existence, it would be meaningless.

> [@Dan](#):
>
> But I know there is no appetite for changes…

Hard disagree, Julia is changing fairly quickly. The key difference is those features are highly demanded and developed collaboratively, not done on unvetted whims.

> [@Dan](#):
>
> ASIDE: Function ending with `for` loops can also replace it with `foreach`

Nope, even if implementing the same algorithm, `foreach`-`do` involves a closure and would introduce type inferrability issues for reassigned captured variables equivalent to outer local variables accessed by a `for`-loop.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [November 10, 2023, 12:52am UTC](https://discourse.julialang.org/t/outer-keyword-to-automatically-local-a-variable/105964/20 "2023-11-10T00:52:39Z")

</div>

> [@Benny](#):
>
> A useless package can generate enough methods ending in for-loops to dwarf every other for-loop in existence, it would be meaningless.

Didn’t mean uniform sampling. Sampling in used packages.

> [@Benny](#):
>
> not done on unvetted whims

For sure.

> [@Benny](#):
>
> highly demanded

Maybe people will like this feature. Julia is Turing-complete without this feature, I agree.
