# Perplexed by behavior of module scope changing variables

**URL:** <https://discourse.julialang.org/t/perplexed-by-behavior-of-module-scope-changing-variables/136075>\
**Category:** New to Julia\
**Tags:** dataframes\
**Created:** [March 6, 2026, 9:43pm UTC](https://discourse.julialang.org/t/perplexed-by-behavior-of-module-scope-changing-variables/136075 "2026-03-06T21:43:27Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Snowy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/snowy/32/36765_2.png) [@Snowy](https://discourse.julialang.org/u/Snowy)\
**Post date:** [March 6, 2026, 9:43pm UTC](https://discourse.julialang.org/t/perplexed-by-behavior-of-module-scope-changing-variables/136075/1 "2026-03-06T21:43:28Z")

</div>

Hello,  
I must be doing something wrong. Can someone explain this behavior to me?

```julia
module A 
    using DataFrames
    x = DataFrame(a=[1,3],b=[4,3])
    export x
end

module B
    using ..A
        function add_stuff(df)
            df[:,:summed_cols] .= sum.(eachrow(df[:,1:2]))
            return(df)
        end
    z = x
    z = add_stuff(z)
end

module C
    using ..A
    x
end

```

running module A:

```julia
julia> A.x
2×2 DataFrame
 Row │ a b     
     │ Int64 Int64 
─────┼──────────────
   1 │ 1 4
   2 │ 3 3

```

^As expected

running module B:

```julia
julia> B.z
2×3 DataFrame
 Row │ a b summed_cols 
     │ Int64 Int64 Int64       
─────┼───────────────────────────
   1 │ 1 4 5
   2 │ 3 3 6

```

^ As expected

running module C:

```julia
julia> C.x
2×3 DataFrame
 Row │ a b summed_cols 
     │ Int64 Int64 Int64
─────┼───────────────────────────
   1 │ 1 4 5
   2 │ 3 3 6

```

^^ **NOT EXPECTED**  
What is happening here? “x” wasn’t even modified in module B, as “z” was put through the function. Why doesn’t C.x look like A.x?

Also, why can module B modify X:

```julia
julia> B.x
2×3 DataFrame
 Row │ a b summed_cols 
     │ Int64 Int64 Int64
─────┼───────────────────────────
   1 │ 1 4 5
   2 │ 3 3 6

```

But if I try to make a modification explicitly to x in the module, e.g.:

```julia
module B
    using ..A
        function add_stuff(df)
            df[:,:summed_cols] .= sum.(eachrow(df[:,1:2]))
            return(df)
        end
    z = x
    z = add_stuff(z)
    x = 3
end

```

I get the error:

```julia
ERROR: cannot assign a value to imported variable A.x from module B

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 6, 2026, 10:38pm UTC](https://discourse.julialang.org/t/perplexed-by-behavior-of-module-scope-changing-variables/136075/2 "2026-03-06T22:38:02Z")

</div>

> [@Snowy](#):
>
> What is happening here? “x” wasn’t even modified in module B, as “z” was put through the function. Why doesn’t C.x look like A.x?

`x` was modified because `df[:,:summed_cols] .= ...` changes `df` in-place.

This has nothing to do with modules or dataframes. A simpler example is:

```julia-auto
julia> f(array) = array[1] = 2; # modifies array in-place

julia> x = [1,1,1]
3-element Vector{Int64}:
 1
 1
 1

julia> f(x);

julia> x # modified by f(x)
3-element Vector{Int64}:
 2
 1
 1

```

I would suggest reading [Assignment expressions and assignments vs. mutation](https://docs.julialang.org/en/v1/manual/variables/#man-assignment-expressions) and [Argument-passing behavior](https://docs.julialang.org/en/v1/manual/functions/#man-argument-passing) carefully. It’s common for people to get confused about 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:** [March 7, 2026, 1:06am UTC](https://discourse.julialang.org/t/perplexed-by-behavior-of-module-scope-changing-variables/136075/3 "2026-03-07T01:06:08Z")

</div>

As an attempt to save some clicks, Julia is one of the languages where variables are only references to objects (on the language-level, the compiler is free to implement it in a variety of ways). When you reassign a variable, you’re only changing what object it’s referencing; mutation of objects is a totally separate action, even if it involves reassignment syntax for elements and fields. This is very different from languages where variables own objects and variable reassignments change those objects.

> [@Snowy](#):
>
> What is happening here? “x” wasn’t even modified in module B, as “z” was put through the function. Why doesn’t C.x look like A.x?

In your specific example, module `A` assigns `A.x`. module `B` imports `x` from `A` then assigns `z = x` (note the implicit `using` only works when there is explicit access, like `z=x`, otherwise it’s possible to assign a different original `x`). `z=x` takes the object assigned to `x` and assigns it to `z`, therefore `x` and `z` reference the same object. `add_stuff` mutates and returns the same object, so `z = add_stuff(z)` reassigned `z` to the object it already was assigned to. module `C` imports `x` from `A`. By the end, `A.x`, `B.x`, `B.z`, and `C.x` all reference the same dataframe. You can verify that the object identities match: `A.x === B.x === B.z === C.x`

> [@Snowy](#):
>
> Why doesn’t C.x look like A.x?

It does, you just didn’t print `A.x` after running module `B`.

> [@Snowy](#):
>
> `ERROR: cannot assign a value to imported variable A.x from module B`

We’re not allowed to reassign imported variables without some qualification. Besides committing to share variables via imports, that makes assignments more confined to home modules to prevent some errors. However, there’s nothing stopping one module from mutating an object assigned to imported variables because that is sharing as intended.
