# Best practice for handling "same" types between different packages

**URL:** <https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451>\
**Category:** New to Julia\
**Created:** [October 18, 2024, 11:03am UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451 "2024-10-18T11:03:25Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 11:03am UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/1 "2024-10-18T11:03:25Z")

</div>

My organization is writing a small ecosystem of packages for physics simulations. The goal is to make them as modular as possible, of course.

One issue we’ve run into is the following: Package A contains some useful stuff, which both Package B and Package C will depend on. However, B will not depend on C. E.g.

```julia
# In file A.jl
module A  
export AType

struct AType{T}
  a::T
end

end

```

```julia
# In file B.jl
module B
include("A.jl")
using .A

struct BType{T}
   atype::AType{T} 
end

BType(a) = BType(AType(a))

end

```

```julia
# In file C.jl
module C
include("A.jl")
using .A

function foo(atype::AType)
  return 10*atype
end

end

```

Now I’d like both B and C to talk to each other. Specifically, I’d like to do

```julia
include("B.jl")
include("C.jl")
using .B, .C

b = B.BType(7)

C.foo(b.atype) # error! Function expecting C.AType not B.AType

```

What is the best way for dealing with this?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [October 18, 2024, 11:20am UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/2 "2024-10-18T11:20:04Z")

</div>

It’s hard to give precise advice when your question is so generic, but I see at least three options:

1. Refactor packages `A` and `B` to depend on a common package that defines the `AType` that you want them both to share.
2. Remove the argument-type from `foo(a)` so that is more “duck-typed” — so that it works for any `a` object that supports certain methods.
3. Write a “glue” package that depends on `A` and `B` and implements a method to convert `BType` into `AType` etc.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 18, 2024, 11:31am UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/3 "2024-10-18T11:31:03Z")

</div>

> [@mattsignorelli](#):
>
> `using A # Separate registered package, not .A`

Huh? AFAIK multiple packages with the same name isn’t supported? What did I miss?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 18, 2024, 11:58am UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/4 "2024-10-18T11:58:08Z")

</div>

> [@mattsignorelli](#):
>
> `C.foo(b.atype) # error! Function expecting C.AType not B.AType`

The way you’ve written it, this would not happen because both modules `B` and `C` imported `A.Atype` from the one package `A`. I assume you have 2 packages named `A` somehow, and there’s no way to make them interchangeable in a nominal type system, even if they have the same structure.

> [@nsajko](#):
>
> AFAIK multiple packages with the same name isn’t supported?

Different packages with the same name can exist in the same dependency graph as long as they are not both direct dependencies of any one package, so `import A` is unambiguous. This is discouraged, especially for duplicate code.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 1:03pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/5 "2024-10-18T13:03:35Z")

</div>

> [@nsajko](#):
>
> Huh? AFAIK multiple packages with the same name isn’t supported? What did I miss?

> [@Benny](#):
>
> I assume you have 2 packages named `A` somehow,

No there are not two packages. Package B has its own instance of A, and Package C has its own instance of A. To my understanding, that is how packages handle dependencies without clashing. The reason I put the explicit comment there about this is because I want to be clear that B and C are two separate packages depending on some other package A, and not two modules sharing some other module A in the higher namespace.

> [@stevengj](#):
>
> It’s hard to give precise advice when your question is so generic, but I see at least three options:

Thanks for your tips! I was mostly checking if there is some Julianic way of doing this, but it seems situation-dependent. My goal above was to give a simple MWE. In actuality our package C will contain a struct which has `AType` as a field. But of course, I cannot construct said type using a `B.A.AType`, only a `C.A.AType`. Therefore it seems the way to go for use is option 3 if my understanding is correct

---

<div class="post-metadata">

**Author:** ![goerz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerz/32/3269_2.png) [@goerz](https://discourse.julialang.org/u/goerz)\
**Post date:** [October 18, 2024, 1:09pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/6 "2024-10-18T13:09:55Z")

</div>

> [@mattsignorelli](#):
>
> Package B has its own instance of A, and Package C has its own instance of A

Modules don’t have “instances”. `A`, `B.A`, and `C.A` are all the same, and can be used interchangeably.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 18, 2024, 1:14pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/8 "2024-10-18T13:14:15Z")

</div>

> [@mattsignorelli](#):
>
> Package B has its own instance of A, and Package C has its own instance of A. To my understanding, that is how packages handle dependencies without clashing. The reason I put the explicit comment there about this is because I want to be clear that B and C are two separate packages depending on some other package A, and not two modules sharing some other module A in the higher namespace.

There are no “instances” here, `A` is unique (if it’s a package). This is some misunderstanding, not sure what your actual original issue is then.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 1:21pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/9 "2024-10-18T13:21:00Z")

</div>

> [@goerz](#):
>
> Modules don’t have “instances”. `A`, `B.A`, and `C.A` are all the same, and can be used interchangeably.

So what happens if package B depends on version 1 of A and C depends on version 2?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 18, 2024, 1:22pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/10 "2024-10-18T13:22:44Z")

</div>

That’s not allowed, at least not currently.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 1:23pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/11 "2024-10-18T13:23:46Z")

</div>

How so? In each package’s Project.toml, I can choose whatever version of A to depend on as I want.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 1:30pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/12 "2024-10-18T13:30:46Z")

</div>

@nsajko @goerz please see the updated comments to the example in my first post, maybe that will clarify what I mean

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [October 18, 2024, 1:33pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/13 "2024-10-18T13:33:14Z")

</div>

You can choose what versions of A each package is compatible with. Whenever another package or environment depends on both B and C, the package manager will try to find a single version of A that both B and C are compatible with. If it can’t find one it gives an error.

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [October 18, 2024, 1:33pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/14 "2024-10-18T13:33:22Z")

</div>

You set compatible package versions in `Project.toml`. The actual versions of packages that are going to be used when running your program are defined by `Manifest.toml`.

When you create a package `P` that depends on `B` and `C`, package `A` will be a (transitive) dependency of `P`, too. Once you instantiate `P`, `Manifest.toml` will be created and versions of all dependencies will be resolved. If `B` fixes `A` to be at version `x` (in `Project.toml` of package `B`), and `C` fixes `A` to be at a different version `y` (in `Project.toml` of package `c`), then the dependencies cannot be resolved and you get an error. However, packages usually define a range of compatible versions of their dependencies, in which case a version for `A` would be found that works for both `B` and `C`. That version is going to be used when running `P`.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 18, 2024, 1:33pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/15 "2024-10-18T13:33:29Z")

</div>

> [@mattsignorelli](#):
>
> How so? In each package’s Project.toml, I can choose whatever version of A to depend on as I want.

Each package has only a single version in a single environment.

> [@mattsignorelli](#):
>
> `The separate registered package defined above`

This sentence doesn’t make sense. The package is separate from what? Each loaded package is unique, as I already wrote.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 1:57pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/16 "2024-10-18T13:57:12Z")

</div>

I modified the example slightly so how about you just run it and see? Here’s the error you’ll get

```julia
julia> C.foo(b.atype)
ERROR: MethodError: no method matching foo(::Main.B.A.AType{Int64})
The function `foo` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  foo(::Main.C.A.AType)
   @ Main.C ~/Test/C.jl:5

Stacktrace:
 [1] top-level scope
   @ REPL[5]:1

```

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [October 18, 2024, 2:02pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/17 "2024-10-18T14:02:12Z")

</div>

That’s a different situation though. Now, you are _including_ a file that defines a module `A` in modules `B` and `C`, which does create two different modules `B.A` and `C.A`. Then the types `B.A.AType` and `C.A.AType` are truly different. That’s not what’s happening when you create a proper package `A` and add it as a dependency to other proper packages `B` and `C`.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 18, 2024, 2:05pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/18 "2024-10-18T14:05:25Z")

</div>

> [@mattsignorelli](#):
>
> `::Main.B.A.AType`

> [@mattsignorelli](#):
>
> `::Main.C.A.AType`

This printing demonstrates you evaluated `module B ... end` and `module C ... end` into `Main`, which indeed may be done by `include`-ing a file. This is not the same thing as installing packages into an environment and importing them in a session. It also demonstrates that you evaluated a `module A ... end` expression twice to make 2 separate modules, which again is not what happens when importing one package, or any module for that matter, several times. To emulate loading the package on the first import then referencing the same module for subsequent imports in the session, evaluate a module once then import it again elsewhere however many times you want.

In the broader picture, some languages like Rust and Javascript do let you use \>1 version of the same package in the same project, with the disadvantages of multiplied memory usage and of the need to segregate or bridge the distinct versions. This is much more of a problem in dynamic languages where users can interactively dig into different packages and throw their types at each other, so it’s notable that Javascript has structural instead of nominal typing. If Julia were structurally typed, then `C.foo(b.atype)` would work if both versions share the structure `struct AType{T} a::T end`. On the other hand, `AType` would also be indistinguishable from `struct Blah{T} a::T end`, which isn’t conducive to type-based dispatch.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [October 18, 2024, 2:59pm UTC](https://discourse.julialang.org/t/best-practice-for-handling-same-types-between-different-packages/121451/19 "2024-10-18T14:59:54Z")

</div>

OK I have tested this by dev-ing out B, and C in the examples here as packages created with PkgTemplates.jl, and using a random package (DualNumbers.jl) for A (letting AType be Dual) and there is no error now. I see my misunderstanding. This is good news as it makes our lives simpler as we develop the ecosystem. Thanks everyone!
