Functions, Modules, and Scope

Alright, so I have been working on an algorithm with lots of variables and lots of repeated function calls, so I put all of my functions into a module and everything works just fine. As I’m nearing the publishing of a paper on this algorithm, I’m trying to do some clean up to make things look nicer and perhaps be less confusing for people who may end up using the algorithm.

The question I have, is this, is there a way to keep my functions in a module, but not have to pass every single variable into the function every time I use it? Let me provide an example.

In the REPL I can make a function that only takes 1 variable as the input, but uses another variable without a problem.

julia> f = X -> X+Y
#9 (generic function with 1 method)

julia> Y=7;

julia> f(3)
10

I have numerous functions that are beyond my ability to craft in a single line but I can also use:

julia> function ff(X)
           X+Y
           end
ff (generic function with 1 method)

julia> Y=7;

julia> ff(3)
10

Now, I’ve got a bunch of different functions, and I don’t want them cluttering the file where the main algorithm is constructed, so I put the function into a module.

module testlib
export ff
function ff(X)
  X+Y
  end
end

So I load up my module to the REPL, but it no longer works. I think this what you call is a scope problem.

julia> using testlib

julia> Y=7;

julia> ff(3)
ERROR: UndefVarError: Y not defined
Stacktrace:
 [1] ff(::Int64) at C:\Users\#######\Documents\GitHub\####\testlib.jl:5
 [2] top-level scope at none:0

I have gotten around this by writing the functions in the module so that they take all of the variables as arguments. i.e.

module testlib
export ff
function ff(X,Y)
  X+Y
  end
end

This works just fine, but is not ascetically pleasing when you have massive amounts of arguments getting passed into functions. I have gone so far as to write data structures to pass these variables around, but its still unwieldy.

Ultimately, I’d like to know, is there a more proper way for me to do this?

Using global variables in this way

function ff(X)
  X+Y
end

is a questionable approach. Unless Y is constant, this will hurt performance. Plus, it is not clear
where that variable comes from, which is a big problem from readability point of view.

2 Likes

Parameters.jl may help.

1 Like

Or Base.@kwdef. That is probably easier, since no extra package is involved.

One way around this is to define Y using a closure

module testlib
export set_Y
function set_Y(Y)
    function ff(X)
        X+Y
    end
    return ff
end
end

and getting the function ff back via

ff = testlib.set_Y(1)

What I’m taking away from this is that I have been doing things the safe way by using:

function ff(X,Y)
 X+Y
end

Which is manageable, if a bit of a pain when lots of variables are involved. I just wanted to be sure I wasn’t missing something silly.

Thanks for the input!