Hi folks!
On my endless quest to make AstroTime.jl the best date and time handling package in the universe, I am pondering another redesign
When modelling my problem domains, I always gravitate to pushing a lot of information into the type system. Letâs have a look at an example:
# We want to model `DateTime`-like objects in different time scales
abstract type AbstractTimeScale end
struct TAI <: AbstractTimeScale end
struct UTC <: AbstractTimeScale end
# Now our epochs can have a `scale` field but need to be parametric
struct Epoch{T<:AbstractTimeScale}
scale::T
# ...
end
In principle, this seems elegant because you can easily dispatch on different AbstractTimeScale
subtypes. There are some less than ideal side effects though.
First of all there is a proliferation of parametric types, e.g., a state vector type which uses Epoch
now needs to have a type parameter for the AbstractTimeScale
to remain concrete. The dynamic dispatch also seems to have a performance impact (?). Another issue is that handling collections of different subtypes of AbstractTimeScale
is painful and requires use of @generated
functions and lots of type annotations in my real world code to remain performant, which I consider a code smell.
Finally, a silly thing but something that bothers me is the fact that this coding style requires lots of empty parens, e.g., Epoch(..., UTC())
. Feels unintuitiveâŚ
TL;DR: Elegant in theory but complex in practice.
A simpler design could just use Symbol
for the time scale, e.g.:
type Epoch
scale::Symbol
end
You lose the benefits of multiple dispatch and need to write âbranchyâ code. It also makes it harder to make the system user-extensible.
function convert_epoch(ep, scale)
if ep.scale === :TAI
if scale === :UTC
tai_to_utc(ep)
elseif ...
...
end
end
end
On the other hand, you do not need to deal with type parameters and generated functions and everything remains concretely typed.
I am not quite sure where I am going with this TBH. I feel like maybe we miss something like Rustâs enum
in Julia, i.e., some form of sum types.
Anyhow, how do you approach these things? Am I overthinking this? Are there situations where you avoid multiple dispatch?