Is "include" safe to use inside a function?

You are free to do it in a .jl file. But you shouldn’t do it in a function. include is for global scope, with your current method, it’s very hard to keep track of scopes. you are making it harder for yourself than necessary.

imo if you are serious about having a front-end for users it’s also a good benefit to limit the API. restricting the user’s input to just the necessary definitions will reduce error.

Ok thanks. I’ll end up doing what you (and the others) suggest, because I want to keep my methods standard according to what everyone says is best practice.

I occasionally use a multiphysics code with a Fortran backend and a Lua interface; the Lua interface is essentially a script that gets included in a global Lua scope which is then loaded bit-by-bit into Fortran. It’s flexible and convenient, and I understand where Damon’s coming from - why put any constraints on the user, so long as their setup file provides the information needed to initialize the state?

@damonturney, I would separate the saving and loading of state into its own set of functions, instead of tying it to individual operations. In most cases, disk I/O will take many orders of magnitude longer than any other operations, so it’s best to only do it when necessary.

Thanks for including that example; I think it is very helpful for understanding what you want. Here are my suggestions:

  1. It seems that all the include does is initialize some variables, whose name and type you are necessarily already aware of. Instead, have the user pass in a named tuple or struct. Here is one example:
    function create_system_state(x)
        include(include_filename)
        system_state = system_state_data_type(x.a, x.b, 789.5)
        # ...
        return system_state
    end

    # the user does:
    create_system_state((a = blah, b = blah))

If passing in a and b as separate arguments makes more sense (or q and r for that matter) just do that instead. As others have noted, this is much better for performance, clarity, “safety”, and sanity.
2. Unless having two separate modules is necessary, I would suggest consolidating them into one. Since simulating and managing the state appear to be entirely codependent operations, having them be a single module seems simpler.
3. It seems strange to me that the simulation saves a file that I then have to read back, rather than being able to do everything in sequence. I.e., I would expect simulate.operate_A to return the Dict directly, so I could inspect/plot/use it. Maybe this was just an example, though.
4. I assume the names are just placeholders, so this is probably moot, but I’ll note anyway that the conventional style is that module names and types are TitleCase. They also tend to be relatively terse and “active”. E.g. rather than system_state_data_type, one would expect SystemState, or even just State (or whatever description is most apt in your case). Rather than manage_system_state, one would expect SystemManager, or Manager, etc., if it’s a submodule, or e.g. BatteryStateManager if it’s the top-level module (in order to be most clear).

Ultimately, the user does:


### the user does:
r=80
q=3
a=[80, 0, 160.0]/r
b=[29.3, 31.1, 3.3]*q

state = create_system_state(a, b)
resulting_system_state = operation_A(state, 13.5, 5)

using Plots
display(plot(resulting_system_state.a, resulting_system_state.b))