Loading from parent scope in package

I have one package that contains all types and another packages that uses those types. Here is the types package:

module MatTypes

using LinearAlgebra
using StaticArrays

include("types.jl")

end # module MatTypes

and here is another package that will use those types:

module MatML


using ..MatTypes:Crystal, DataSet

using StaticArrays

include("LennardJones.jl")
#include("metrop.jl")

end # module MatML

Both of these modules have been turned into packages and added as dependencies.

Notice that in MatML, I’m trying to use the types from the parent scope, not directly from MatTypes. It needs to be this way because objects with these types are going be passed around to functions in various packages so they need to be the same type.

My problem: When I load the second package it doesn’t recognize MatTypes, even though it’s been loaded in the parent scope. In other words:

using MatTypes # Successful 
using MatML # Failed: UndefVarError:MatTypes not defined.

Well, MatML must have MatTypes as dependency…

If you go to the project MathML, activate it and type:

using Pkg
Pkg.add("MatTypes")

Does that work?

Of course you could also use a local path for Pkg.add if that is what you want…

It does…


Activating project at `~/MatML`

Status `~/MatML/Project.toml`
  [b127de0a] MatTypes v0.1.0 `/codes/MatTypes`


If I

using MatTypes:Crystal, DataSet

instead of

using ..MatTypes:Crystal, DataSet

It loads just fine.

1 Like

So is the issue solved?

No. I need to use the Crystal type defined in the parent scope.

Why? What is that good for?

To be honest, I don’t think that this is possible with Julia.

I have functions in two different packages that have arguments of type Crystal. If I load Crystal inside the MatML module then its functions are expecting a type MatML.Crystal. But I’m using another module/package to build the variables of type Crystal and when I pass those variables in to the MatML function it doesn’t recognize it as the right type.

Well, they also need to have a common, abstract super-type, don’t they?

I have:

  • package A defining AbstractKiteModel
  • package B depending on A defining a GreenKiteModel <: AbstractKiteModel
  • package C depending on A defining a RedKiteModel <: AbstractKiteModel
  • package D depending on A and B or A and C defining operations on AbstractKiteModels

Would that work for you?

Let me see if I can make a simplified example. Here’s one module with a function that returns a variable of type Crystal.

module VASP

using MatTypes

function buildCrystal(file::String)

## Does some stuff and returns a Crystal object.
return Crystal(...)

end

end

And here is another module with a function that takes an argument of type Crystal

module analyze

using MatTypes

function samples(crystal::Crystal)

## Does some stuff

end

end

Now if I try to call functions from both of these modules like this:

# Main

using VASP
using analyze

filename = "thisfile.out"
myCrystal = buildCrystal(filename)
sample(myCrystal)

I get an error because the variable that buildCrystal returned had type VASP.Crystal but the function named sample is expecting a variable with type analyze.Crystal

My solution is to load the Crystal type from the parent scope in both modules so they refer to the same type…
Is that making any sense?

Well, you need to define a common abstract type AbstractCrystal …

All Chrystals must have something in common, and you can only define operations on fields they have in common…

Hmm. I see what you’re saying, but not sure how to do it… :slight_smile:

This is my definition of the type KPS4: KiteModels.jl/src/KPS4.jl at 36be0d9ea438442d20b7976670b2828d4a468123 · aenarete/KiteModels.jl · GitHub

of the type KPS3: KiteModels.jl/src/KPS3.jl at 36be0d9ea438442d20b7976670b2828d4a468123 · aenarete/KiteModels.jl · GitHub

and of AbstractKiteModel: KiteModels.jl/src/KiteModels.jl at 36be0d9ea438442d20b7976670b2828d4a468123 · aenarete/KiteModels.jl · GitHub

in case this is of any help…

1 Like

Thank you. Would you say this is the “correct” way to solve this particular problem; can you see any advantages/disadvantages to doing this over just always loading from the parent scope?

Well, as far as I know “loading from parent scope” will not work with Julia packages, it will only work when you use includes.

The question is, how closely or how loose you want to have your code components coupled. For loose coupling use abstract types.

Not sure what you mean when you say “won’t work”. Isn’t the double dot syntax used to load from the parent scope?

using ..StaticArrays:SVector

I don’t think that is valid in a package…

Well I guess that explains my original problem. That only works in modules?

I guess so, but I am not an expert…

If only one type is needed (Crystal, in this case), there shouldn’t be any need for a type hierarchy. Unless I misunderstood something?

Short answer: yes, the dots are only there for submodules to access their parent modules.

Here is a longer answer that could maybe clear things up a bit:
With using MatTypes (without any dot), Julia has to perform two things:

  1. Look in the package environment for a dependency named MatType, and load the associated code. This step is called code loading.
  2. Look inside the MatType module for exported functions/types, and make them available in the current module.

If several modules/packages do using MatTypes, Julia makes sure code loading will happen only exactly once. This means that MatType.Crystal should be the same type in all contexts where the MatType package is used (this is in contrast to a common mistake where beginners would include the same source file in several modules, leading to different types being created in all modules).

With the dotted syntax (using .MatTypes), you’re telling Julia to skip the code loading step. Instead, you’re saying: “no need to load any more code, simply look in my parent module, there should already be a MatType module there. Simply make any exported name available to me”.

This shouldn’t happen. If several packages do using MatTypes: Crystal in a correctly instantiated environment, they should all be referring to the same MatTypes.Crystal type.

How did you set things up in terms of dependency management? IIUC you have at least 4 projects:

  • MatTypes (a full package)
  • VASP (full package as well)
  • Analyze (again full package)
  • Main? (some project that uses both VASP and Analyze)

Which Pkg commands did you use to declare dependencies between them?

1 Like