Declare Union type as a function of which packages are installed

parametric-types
#1

Here’s my use case: I have a series of parametric types, Foo{T}, and I would like them to accept different values of T depending on whether or not some packages are installed.

My current code is as follows:

_allowed_species_types = [String, Symbol, MangalNode, MangalReferenceTaxon]
try
  using GBIF
  push!(_allowed_species_types, GBIF.GBIFTaxon)
  @info "Package GBIF found: GBIFTaxon added as a species type"
catch
  nothing
end

"""
...
"""
AllowedSpeciesTypes = Union{_allowed_species_types...}

Is this the best way to proceed?

#2

Probably not; manipulating types themselves depending on (external) global state may be a suboptimal design.

Knowing more about the context (what you are trying to do) could lead to better suggestions.

#3

I have graphs, and nodes can have different types. But I want to restrict these types somehow, or allow some types to be used as nodes.

#4

The problem with making the type definition conditional is that it’s going to make your code hard to reason about and potentially dependent on the order in which you load or install packages.

Instead, how about this:

Drop the restriction on T entirely, and just let it be anything as far as the type definition is concerned. Your struct should look like:

struct Foo{T}
  x::T
  function Foo{T}(x::T) where {T}
     ... inner constructor goes here...
  end
end

Then, in the inner constructor, add a check like this:

is_allowed(T) || throw(ArgumentError("Not allowed"))

Now you need to define:

is_allowed(::Type{T}) where {T} = false
is_allowed(::Type{String}) = true

For types you want to conditionally allow, use Requires.jl to do (roughly):

@require OtherPackage begin
  is_allowed(::Type{OtherPackage.Foo}) = true
end

This will give you good performance (the is_allowed check can likely be optimized away by the compiler) and no weird dependency on load order.

5 Likes
#5

Either use subtyping (all have a common abstract supertype) , duck typing (no type restriction), or verify something at runtime. I would very much discourage trying to do almost anything based on what packages happen to be installed and especially try to constrain types based on it.

3 Likes