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
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…
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:
- Look in the package environment for a dependency named
MatType
, and load the associated code. This step is called code loading.
- 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 export
ed 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