How to include into local scope?

In quick and dirty development/testing, the ability to just copy-paste code is very handy. If a snippet is used a lot, it can be elevated to a function later.

Is there a way to evaluate a file in local scope? The Base.include function evaluates in the global scope, which is not what I want. For a similar discussion, see Is "include" safe to use inside a function?

For example, say I have some complicated plotting routine in a file wowPlot.jl:

display(x^2)

I create some elaborate data and display it:

x = 5
include("wowPlot.jl")
25

Then I want a local scope to not mess with the original data:

let x = 6
    include("wowPlot.jl")
end
25

How to make the last statement display 36?

You can’t. And this is what function/macros are for.

1 Like

Is there some fundamental reason why it is not possible? Is it worth requesting this feature? I would find it very useful.

Right now I can’t think of a single language where this is not possible.

module LocalScope
    x = 6
    include("wowPlot.jl")
end
3 Likes

You suggest using modules as a super-let? Interesting!

Yes, include is a global scope and you cannot make itt a local scope. Same as that you cannot make eval local.

No, it’s described in the thread you linked.

C.

Module creates a global scope. Yes you can do that but you shouldn’t.

If this is actually you goal, you should just use a function.

Maybe we are talking past each other here. In C—which the preprocessor is an integral part of—an #include does exactly what I want.

Many people are using Julia for many things. Right now I am doing jupyter scripts and this kind of raw paste would be really handy.

Include into local scope, would be the same as eval into local scope.
eval into local scope is one of the things that makes code impossible to optimize.
It is a feature julia very intentionally doesn’t have.

3 Likes

Yes, I love Julias compiled-language side. But I think interpreted, throwaway scripts are also very important for a modern language. I appreciate that some functions are optimized to the bitter end, but I would also like to have a interpreted interface that is more flexible. Like Python, perhaps.

I recommend against this, but I think the following would work:

macro include(filename::String)
    dir = dirname(string(__source__.file))
    filepath = joinpath(dir, filename)
    source = "quote; " * read(filepath, String) * "; end"
    esc(Meta.parse(source).args[1])
end

e.g.

function foo(x)
    @include "body.jl"
end

Which is a close approximation to what C’s #include does.
I feel like it is a unwise thing, and I would say the exact same about putting a local #include in a C file.

Note that this not catch changes in the file included.

Whats wrong with functions?

2 Likes

Sure, you can do that in Julia too.

macro include(filename::AbstractString)
    path = joinpath(dirname(String(__source__.file)), filename)
    return esc(Meta.parse("quote; " * read(path, String) * "; end").args[1])
end

then

let x = 5
    @include "wowPlot.jl"
end

or

function f(x)
    @include "wowPlot.jl"
end

will do what you expect (i.e. act as if you pasted the included code at that point). (@oxinabox had the same idea and was a bit quicker to post, but I had a chance to test mine and I believe it works.)

(I wouldn’t recommend this from a maintenance/reability standpoint, however! And even for short-term hacks I think it is better to write functions as soon as you find yourself re-using code.)

4 Likes

Exactly. If you want to do the equivalent to preprorcessor tricks, the analogy in julia is macros - which are safer and more powerful.

However… if you have some reusable code, thee is a 99% chance it is better off in a function in a module than a literal include.

1 Like

Yes, copy paste is fine. But I don’t see what’s wrong with a function.

2 Likes

Thank you for your answers.

The consensus seems to be that everything should be packaged into a function. I totally agree.

I will try to describe my use case without actually dumping the code here. I am building a plot that visualises 10 different things. The function would look like

function wowPlot(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, parameters, more_parameters)

This would not be a good function. I hope you agree. Currently in my script I use all those variables and parameters and build a nice plot. Then I modify some variables or some parameters and plot again and compare the difference. Currently I just copy-paste the plotting code to all places in all scripts where I need it. It would be cleaner to be able to simply raw-include into scripts. I you have solved this problem in some other way I would love to hear about it.

Can’t you aggregate some arguments into vectors, tuples or namedtuples and pass around those?

1 Like
@include "testInclude.jl"
type QuoteNode has no field file

Stacktrace:
 [1] getproperty(::QuoteNode, ::Symbol) at ./Base.jl:20
 [2] @include(::LineNumberNode, ::Module, ::String) at ./In[1]:2nd_place_medal: 

Nothing more wrong for this function than doing worse by using including a file.

3 Likes

Someting like

t = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, parameters, more_parameters)
wowPlot(t)

You mean?

And then unpack inside the function? I think this is just syntax. The semantics of wowPlot are still the same and still too broad to be a good function.

Are you saying that I should casually write functions with more parameters that can fit on a line? My point is that I want a separation between good and proper functions on one hand and quick and dirty scripting on the other. This post is about making the scripting easier.

It sounds like you have default values for those parameters. In that case you can use keyword arguments:

function wowPlot(; x1=..., x2=...., ....)
   ...
end

and then when you call it you only need to pass the parameters you want to change.

1 Like