# Generate a list of functions

**URL:** <https://discourse.julialang.org/t/generate-a-list-of-functions/80691>\
**Category:** General Usage\
**Tags:** question, scope\
**Created:** [May 8, 2022, 2:22am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691 "2022-05-08T02:22:09Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![zhaiyusci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zhaiyusci/32/13125_2.png) [@zhaiyusci](https://discourse.julialang.org/u/zhaiyusci)\
**Post date:** [May 8, 2022, 2:22am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/1 "2022-05-08T02:22:10Z")

</div>

Hello everyone!  
I want to generate a list of functions, and run into some unexpected behaviour.

```julia
# Case 1: using anonymous functions
fs=[]
for i in 1:3
    f=()->i
    push!(fs,f)
end
@show [p() for p in fs]

# Case 2: create function with a new name
gs=[]
for i in 1:3
    function g()
        i
    end
    push!(gs,g)
end
@show [p() for p in gs]

# Case 3: create function with a used name
h()=100
hs=[]
for i in 1:3
    function h()
        i
    end
    push!(hs,h)
end
@show [p() for p in hs]

```

The output is like

```nohighlight
[p() for p = fs] = [1, 2, 3]
[p() for p = gs] = [1, 2, 3]
[p() for p = hs] = [3, 3, 3]

```

Apparently, the behaviour of Julia in case 3 is different from that in case 2. Although the definition of `h()` is my sample code is fairly close to the loop, in real program, it can be in pretty far ahead, or in other files, which may cause some issues.

Also, we tell from the output, the `h()` function is neither the expected ones as in case 2, nor the one defined outside the loop, which should return `100`.

I am not sure whether this behaviour is documented somewhere or not. If it is already documented, a warning should be given here.

I tested code above using Julia 1.7.1 on Ubuntu 20.04.

With best wishes,  
Yu

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 8, 2022, 3:05am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/2 "2022-05-08T03:05:54Z")

</div>

I think the for-loop scope rule is messing with you. When you have `h()` from before the for loop, and use (by `push!()`ing it) it inside the loop, somehow Julia decides to evaluate the function definition in global scope.

```julia

julia> g()
ERROR: UndefVarError: g not defined
Stacktrace:

julia> h()
3

```

---

<div class="post-metadata">

**Author:** ![zhaiyusci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zhaiyusci/32/13125_2.png) [@zhaiyusci](https://discourse.julialang.org/u/zhaiyusci)\
**Post date:** [May 8, 2022, 3:12am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/4 "2022-05-08T03:12:54Z")

</div>

Thank you for the reply.  
In such case, I would expect three 100 output, if the global `h` is used, as I defined `h()=100`.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 8, 2022, 4:42am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/5 "2022-05-08T04:42:42Z")

</div>

why? I mean, it’s the same principle as:

```julia
julia> gs=[]
Any[]

julia> for i in 1:3
           g = i
           push!(gs,g)
       end
julia> g
ERROR: UndefVarError: g not defined

julia> gs
3-element Vector{Any}:
 1
 2
 3

julia> h = 100
100

julia> hs = []
Any[]

julia> for i in 1:3
           h = i
           push!(hs,h)
       end

julia> hs
3-element Vector{Any}:
 1
 2
 3

julia> h
3

```

except the global `h()` function is evaluated later in `[h() for h in hs]`

---

<div class="post-metadata">

**Author:** ![zhaiyusci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zhaiyusci/32/13125_2.png) [@zhaiyusci](https://discourse.julialang.org/u/zhaiyusci)\
**Post date:** [May 8, 2022, 4:56am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/6 "2022-05-08T04:56:57Z")

</div>

I remove the misleading `[h() for h in hs]` and replace it as `[p() for p in hs]`. The problem is not located in the `@show` lines.

The non-function analogy is like

```julia
x=100
xs=[]
for i in 1:3
    x=i
    push!(xs,x)
end
@show [p for p in xs]
@show x

```

and the output is

```nohighlight
[p for p = xs] = [1, 2, 3]
x = 3

```

which is reasonable, because the global `x` is rebound to the value of for-loop local `i`, and after the loop, `x==3`.

While in my “list of function” sample, if the rule in non-function case hold, I would expect the function in the list returns `[1, 2, 3]`, or the global function `f()` is used, returns `[100, 100, 100]`. But none of these happens. Instead, the final answer is `[3, 3, 3]`, it seems the `i` is stuck to the last iteration of the loop, while it is not in case 2.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 8, 2022, 5:05am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/7 "2022-05-08T05:05:31Z")

</div>

a better analogy may be this?

```julia
julia> hs = []
Any[]

julia> h = [100]
1-element Vector{Int64}:
 100

julia> for i in 1:3
           h[] = i
           push!(hs,h)
       end

julia> hs
3-element Vector{Any}:
 [3]
 [3]
 [3]

```

you’re calling the function `h()` only after the list is constructed. You can only have one `h()` (a named function) in the Main scope, so when you evaluate it after the loop, by construction you would get the same value.

Becuase `h` existed before the loop and you use `h` on the RHS in the loop, it all refers to the same `h` – the named function `h()` in Main scope.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 8, 2022, 5:11am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/8 "2022-05-08T05:11:13Z")

</div>

or, if you evaluate the `h()` inside the `push()`, it will reproduce the example when `h=i` is just an immutabe scalar:

```julia
julia> h()=100
h (generic function with 1 method)

julia> hs=[]
Any[]

julia> for i in 1:3
           function h()
               i
           end
           push!(hs,h())
       end

julia> hs
3-element Vector{Any}:
 1
 2
 3

```

basically you re-define the `Main.h()` in each iteration, and call `Main.h()` each iteration as well, instead of delaying the evaluation after the loop.

---

<div class="post-metadata">

**Author:** ![zhaiyusci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zhaiyusci/32/13125_2.png) [@zhaiyusci](https://discourse.julialang.org/u/zhaiyusci)\
**Post date:** [May 8, 2022, 5:16am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/9 "2022-05-08T05:16:08Z")

</div>

Got the idea. `Main.h` is modified, and all three element in the `hs` refers to it.

---

<div class="post-metadata">

**Author:** ![zhaiyusci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zhaiyusci/32/13125_2.png) [@zhaiyusci](https://discourse.julialang.org/u/zhaiyusci)\
**Post date:** [May 8, 2022, 6:47am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/10 "2022-05-08T06:47:25Z")

</div>

Although somehow I understand this analogy… Is that means a function is mutable?  
When I am ready to accept this, I find

```julia
f()=1
@show ismutable(f)

```

gives false.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [May 8, 2022, 3:10pm UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/11 "2022-05-08T15:10:47Z")

</div>

I think this may be a semantics problem. `ismutable` probably expects an instance of a `struct` or something like that. The `ismutable` documentation says:

> ismutable(v) → Bool
> 
> Return true iff value v is mutable. See Mutable Composite Types for a discussion of immutability. Note that this function works on values, so if you give it a type, it will tell you that a value of DataType is mutable.

Functions are mutable in the sense new methods can be added to them, and their previous methods can be redefined.

---

<div class="post-metadata">

**Author:** ![brianguenter](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brianguenter/32/29519_2.png) [@brianguenter](https://discourse.julialang.org/u/brianguenter)\
**Post date:** [May 8, 2022, 5:51pm UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/12 "2022-05-08T17:51:38Z")

</div>

> [@zhaiyusci](#):
>
> `@show [p() for p in hs]`

If I’m interpreting the documentation correctly this assignment case is ambiguous and will give different results when evaluated in interactive vs. non-interactive cases:

''When `x = <value>` occurs in a local scope, Julia applies the following rules to decide what the expression means based on where the assignment expression occurs and what `x` already refers to at that location:

1. **Existing local:** If `x` is _already a local variable_ , then the existing local `x` is assigned;
2. **Hard scope:** If `x` is _not already a local variable_ and assignment occurs inside of any hard scope construct (i.e. within a `let` block, function or macro body, comprehension, or generator), a new local named `x` is created in the scope of the assignment;
3. **Soft scope:** If `x` is _not already a local variable_ and all of the scope constructs containing the assignment are soft scopes (loops, `try` / `catch` blocks, or `struct` blocks), the behavior depends on whether the global variable `x` is defined:\*\*  
\*\* \* if global `x` is _undefined_ , a new local named `x` is created in the scope of the assignment;\*\*  
\*\* \* **if global `x` is _defined_ , the assignment is considered ambiguous:**  
\*\* \* in _non-interactive_ contexts (files, eval), an ambiguity warning is printed and a new local is created;\*\*  
\*\* \* in _interactive_ contexts (REPL, notebooks), the global variable `x` is assigned.\*\*

You may note that in non-interactive contexts the hard and soft scope behaviors are identical except that a warning is printed when an implicitly local variable (i.e. not declared with `local x` ) shadows a global. In interactive contexts, the rules follow a more complex heuristic for the sake of convenience. This is covered in depth in examples that follow.‘’

I created a file containing the code:

```julia
module App

h()=100
hs=[]
for i in 1:3
    function h()
        i
    end
    push!(hs,h)
end
@show [p() for p in hs]
println("global h function $(h())")

end # module

```

and then did a non-interactive evaluation

```julia
julia> using App
[Info: Precompiling App [4c3466e5-b909-4ae6-bfb7-8e1039e25e36]
[p() for p = hs] = [1, 2, 3]
global h function 100

```

In this case a new function h() was bound each time through the loop and the global h() was unaffected as @zhaiyusci expected.

---

<div class="post-metadata">

**Author:** ![zhaiyusci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zhaiyusci/32/13125_2.png) [@zhaiyusci](https://discourse.julialang.org/u/zhaiyusci)\
**Post date:** [May 9, 2022, 7:05am UTC](https://discourse.julialang.org/t/generate-a-list-of-functions/80691/13 "2022-05-09T07:05:51Z")

</div>

> [@brianguenter](#):
>
> ```julia
> julia> using App
> [Info: Precompiling App [4c3466e5-b909-4ae6-bfb7-8e1039e25e36]
> [p() for p = hs] = [1, 2, 3]
> global h function 100
> 
> ```

Great! You reminded me that my concern is related to a scope issue.  
I forgot to test my example in a non-interactive environment, but only in a Jupyter notebook.  
Interestingly, the soft-local-scope warning is not raised if a function in local scope is redefined, as in Julia 1.7. I am not sure whether it is a pitfall in JuliaLang. But a warning will help.
