Well, I’m sorry to derail this further but, on the other hand, I feel like this proposal is more likely to be prioritized if people understand why it is so useful to you.
One problem I see with the way you do things, is that the same code is include
d in several different contexts, creating types (Type1
and Type2
) and functions (f
) that should be the same, but actually are not. To be clear: in what you describe above, SystemA.MyTypes.Type1
and SystemB.MyTypes.Type1
are two different types that happen to have the same structure. I would say that this could cause unexpected behavior later on, especially if these types are supposed to be part of a common interface.
Here is how I would structure things:
# Only one MyTypes module
module MyTypes
export Type1
struct Type1; field::Int; end
end
# Only one SubSystem module
module SubSystem
using ..MyTypes
export foo
foo(x::Type1) = x.field
end
# Module SystemA uses the top-level MyType module
module SystemA
using ..MyTypes
using ..SubSystem
test() = foo(Type1(1))
end
module SystemB
# This makes MyTypes and SubSystem available in the current namespace
# ...and therefore also in submodules, using the .. syntax
import ..MyTypes
import ..SubSystem
# Submodules of SystemB can also use the same top-level MyType module
# without having to know about their depth in the module hierarchy
module SomeOtherSubSystem
using ..MyTypes
using ..SubSystem
export bar
bar(x::Type1) = 2*foo(x)
end
using ..MyTypes
using .SomeOtherSubSystem
test() = bar(Type1(1))
end
SystemA.test() # --> 1
SystemB.test() # --> 2
Now, I would even go one step further and imagine that everything is in its own package. In a real application, the code base should reside in a package, that defines a top-level module of the same name. When the package environment is activated, this top-level module can be referred to in absolute syntax (without leading dots) from anywhere. Therefore, submodules can also be accessed to using an absolute path starting from the top-level module.
The following example is designed to run in the REPL, where everything is defined in the Main
module. If you want to transpose it to a project, simply replace every occurrence of Main
with the name of the top-level module:
# Only one MyTypes module
module MyTypes
export Type1
struct Type1; field::Int; end
end
module SubSystem
# Refer to MyTypes using an absolute path starting with the top-level module
# (replace Main with the name of your top-level module, i.e. the name of the package)
using Main.MyTypes
export foo
foo(x::Type1) = x.field
end
module SystemA
using Main.MyTypes
using Main.SubSystem
test() = foo(Type1(1))
end
module SystemB
module SomeOtherSubSystem
# No need to know how deep we are, since we now use absolute module paths
using Main.MyTypes
using Main.SubSystem
export bar
bar(x::Type1) = 2*foo(x)
end
using Main.MyTypes
using .SomeOtherSubSystem
test() = bar(Type1(1))
end
SystemA.test() # --> 1
SystemB.test() # --> 2
Again, this has almost nothing to do with your proposed syntax and I’m sorry for the thread derailment, but would such a structure make sense in your project?